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
3 changes: 2 additions & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"test:app-list-output-text": "bun test/test-app-list-output-text.mjs",
"test:doctor-analytics": "bun test/test-doctor-analytics.mjs",
"test:posthog-exception": "bun test/test-posthog-exception.mjs",
"test:cli-recovery": "bun test/test-cli-recovery.mjs",
"test:create-supabase-client": "bun test/test-create-supabase-client.mjs",
"test:onboarding-recovery": "bun test/test-onboarding-recovery.mjs",
"test:onboarding-progress": "bun test/test-onboarding-progress.mjs",
Expand Down Expand Up @@ -171,7 +172,7 @@
"test:android-version": "bun test/test-android-version.mjs",
"test:platform-flow-contract": "bun test/test-platform-flow-contract.mjs",
"test:tail-engine-shared": "bun test/test-tail-engine-shared.mjs",
"test": "bun run build && bun run test:helper-dce && bun run test:version-detection:setup && bun run test:bundle && bun run test:bundle-validation && bun run test:functional && bun run test:semver && bun run test:auto-bump-version && bun run test:auto-bump-ai-diff && bun run test:version-edge-cases && bun run test:regex && bun run test:upload && bun run test:fail-on-incompatible && bun run test:native-dependencies && bun run test:package-json-guard && bun run test:credentials && bun run test:credentials-validation && bun run test:android-service-account-validation && bun run test:build-zip-filter && bun run test:checksum && bun run test:build-needed && bun run test:ci-prompts && bun run test:ci-secrets && bun run test:android-onboarding-progress && bun run test:onboarding-telemetry && bun run test:v2-event-migration && bun run test:analytics && bun run test:cli-headers && bun run test:authenticated-command-invocation && bun run test:analytics-error-category && bun run test:analytics-org-resolver && bun run test:supabase-perf && bun run test:preview-qr && bun run test:app-set-options && bun run test:mcp-analytics && bun run test:mcp-instructions && bun run test:mcp-live-update-onboarding && bun run test:mcp-stdout-guard && bun run test:mcp-platform-select && bun run test:mcp-explain-scopes && bun run test:mcp-oauth-reopen && bun run test:mcp-broker-oauth && bun run test:mcp-broker-session && bun run test:mcp-credentials-manage && bun run test:mcp-resume-prompt && bun run test:mcp-build-job && bun run test:mcp-build-tools && bun run test:app-created-source && bun run test:app-list-output-text && bun run test:doctor-analytics && bun run test:posthog-exception && bun run test:create-supabase-client && bun run test:build-platform-selection && bun run test:builder-project-discovery && bun run test:onboarding-recovery && bun run test:onboarding-progress && bun run test:onboarding-run-targets && bun run test:run-device-command && bun run test:init-monorepo-targeting && bun run test:init-app-conflict && bun run test:channel-add-exists && bun run test:wait-log && bun run test:init-guardrails && bun run test:init-replay && bun run test:init-telemetry && bun run test:prompt-preferences && bun run test:esm-sdk && bun run test:mcp && bun run test:mcp-no-key-handshake && bun run test:auth-session && bun run test:version-detection && bun run test:platform-paths && bun run test:project-type-detection && bun run test:payload-split && bun run test:manifest-path-encoding && bun run test:macos-signing && bun run test:asc-key-protocol && bun run test:apple-api-import-helpers && bun run test:apple-api-verify-key && bun run test:bundle-id-detector && bun run test:apple-api-app-list && bun run test:app-verification && bun run test:pbxproj-parser && bun run test:ai-log-capture && bun run test:ai-analyze-flow && bun run test:cicd-failure-help && bun run test:ai-sse-parser && bun run test:ai-render-markdown && bun run test:ai-stream-markdown && bun run test:ai-onboarding-mode && bun run test:ai-fit && bun run test:platform-layout && bun run test:frame-fit && bun run test:onboarding-min-size && bun run test:min-size-gate && bun run test:shell-size-gate && bun run test:build-log-sanitize && bun run test:build-output-viewport && bun run test:diff-viewer-viewport && bun run test:build-complete-exit && bun run test:ai-analyze-stream && bun run test:support-mailto && bun run test:support-redact && bun run test:support-internal-log && bun run test:support-help-menu && bun run test:support-contact && bun run test:support-upload-prompt && bun run test:support-bundle-files && bun run test:self-update && bun run test:update-prompt && bun run test:apple-api-cert-create && bun run test:android-tail-engine && bun run test:android-tail-render && bun run test:android-tail-routing && bun run test:dev-gate-stripped && bun run test:frame-fit-ios-shared && bun run test:ios-confirm-app-id && bun run test:ios-create-new && bun run test:ios-e2e && bun run test:ios-flow-contract && bun run test:ios-import-discovery && bun run test:ios-import-export && bun run test:ios-import-pickers && bun run test:ios-import-recovery && bun run test:ios-recovery && bun run test:ios-resume && bun run test:ios-tail-handoff && bun run test:ios-tui-render && bun run test:p8-error && bun run test:ios-tui-routing && bun run test:ios-updater-sync-validation && bun run test:ios-verify-app && bun run test:ios-marketing-version && bun run test:android-version && bun run test:platform-flow-contract && bun run test:tail-engine-shared && bun run test:prescan && bun run test:android-reporting-api && bun run test:android-app-verification && bun run test:android-rename && bun run test:appflow-auth && bun run test:appflow-api-map && bun run test:appflow-validate && bun run test:appflow-flow && bun run test:appflow-gapfill && bun run test:appflow-engine && bun run test:appflow-tail && bun run test:appflow-fetch && bun run test:appflow-sa-decode && bun run test:app-permission-helper && bun run test:2fa-compliance-network && bun run test:organization-set-api-host && bun run test:trial-warning && bun run test:plan-validation",
"test": "bun run build && bun run test:helper-dce && bun run test:version-detection:setup && bun run test:bundle && bun run test:bundle-validation && bun run test:functional && bun run test:semver && bun run test:auto-bump-version && bun run test:auto-bump-ai-diff && bun run test:version-edge-cases && bun run test:regex && bun run test:upload && bun run test:fail-on-incompatible && bun run test:native-dependencies && bun run test:package-json-guard && bun run test:credentials && bun run test:credentials-validation && bun run test:android-service-account-validation && bun run test:build-zip-filter && bun run test:checksum && bun run test:build-needed && bun run test:ci-prompts && bun run test:ci-secrets && bun run test:android-onboarding-progress && bun run test:onboarding-telemetry && bun run test:v2-event-migration && bun run test:analytics && bun run test:cli-headers && bun run test:authenticated-command-invocation && bun run test:analytics-error-category && bun run test:analytics-org-resolver && bun run test:supabase-perf && bun run test:preview-qr && bun run test:app-set-options && bun run test:mcp-analytics && bun run test:mcp-instructions && bun run test:mcp-live-update-onboarding && bun run test:mcp-stdout-guard && bun run test:mcp-platform-select && bun run test:mcp-explain-scopes && bun run test:mcp-oauth-reopen && bun run test:mcp-broker-oauth && bun run test:mcp-broker-session && bun run test:mcp-credentials-manage && bun run test:mcp-resume-prompt && bun run test:mcp-build-job && bun run test:mcp-build-tools && bun run test:app-created-source && bun run test:app-list-output-text && bun run test:doctor-analytics && bun run test:posthog-exception && bun run test:cli-recovery && bun run test:create-supabase-client && bun run test:build-platform-selection && bun run test:builder-project-discovery && bun run test:onboarding-recovery && bun run test:onboarding-progress && bun run test:onboarding-run-targets && bun run test:run-device-command && bun run test:init-monorepo-targeting && bun run test:init-app-conflict && bun run test:channel-add-exists && bun run test:wait-log && bun run test:init-guardrails && bun run test:init-replay && bun run test:init-telemetry && bun run test:prompt-preferences && bun run test:esm-sdk && bun run test:mcp && bun run test:mcp-no-key-handshake && bun run test:auth-session && bun run test:version-detection && bun run test:platform-paths && bun run test:project-type-detection && bun run test:payload-split && bun run test:manifest-path-encoding && bun run test:macos-signing && bun run test:asc-key-protocol && bun run test:apple-api-import-helpers && bun run test:apple-api-verify-key && bun run test:bundle-id-detector && bun run test:apple-api-app-list && bun run test:app-verification && bun run test:pbxproj-parser && bun run test:ai-log-capture && bun run test:ai-analyze-flow && bun run test:cicd-failure-help && bun run test:ai-sse-parser && bun run test:ai-render-markdown && bun run test:ai-stream-markdown && bun run test:ai-onboarding-mode && bun run test:ai-fit && bun run test:platform-layout && bun run test:frame-fit && bun run test:onboarding-min-size && bun run test:min-size-gate && bun run test:shell-size-gate && bun run test:build-log-sanitize && bun run test:build-output-viewport && bun run test:diff-viewer-viewport && bun run test:build-complete-exit && bun run test:ai-analyze-stream && bun run test:support-mailto && bun run test:support-redact && bun run test:support-internal-log && bun run test:support-help-menu && bun run test:support-contact && bun run test:support-upload-prompt && bun run test:support-bundle-files && bun run test:self-update && bun run test:update-prompt && bun run test:apple-api-cert-create && bun run test:android-tail-engine && bun run test:android-tail-render && bun run test:android-tail-routing && bun run test:dev-gate-stripped && bun run test:frame-fit-ios-shared && bun run test:ios-confirm-app-id && bun run test:ios-create-new && bun run test:ios-e2e && bun run test:ios-flow-contract && bun run test:ios-import-discovery && bun run test:ios-import-export && bun run test:ios-import-pickers && bun run test:ios-import-recovery && bun run test:ios-recovery && bun run test:ios-resume && bun run test:ios-tail-handoff && bun run test:ios-tui-render && bun run test:p8-error && bun run test:ios-tui-routing && bun run test:ios-updater-sync-validation && bun run test:ios-verify-app && bun run test:ios-marketing-version && bun run test:android-version && bun run test:platform-flow-contract && bun run test:tail-engine-shared && bun run test:prescan && bun run test:android-reporting-api && bun run test:android-app-verification && bun run test:android-rename && bun run test:appflow-auth && bun run test:appflow-api-map && bun run test:appflow-validate && bun run test:appflow-flow && bun run test:appflow-gapfill && bun run test:appflow-engine && bun run test:appflow-tail && bun run test:appflow-fetch && bun run test:appflow-sa-decode && bun run test:app-permission-helper && bun run test:2fa-compliance-network && bun run test:organization-set-api-host && bun run test:trial-warning && bun run test:plan-validation",
"test:build-platform-selection": "bun test/test-build-platform-selection.mjs",
"test:builder-project-discovery": "bun test/test-builder-project-discovery.mjs",
"test:ai-log-capture": "bun test/test-ai-log-capture.mjs",
Expand Down
5 changes: 5 additions & 0 deletions cli/src/api/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
constants,
createCipheriv,
createDecipheriv,
createPublicKey,
generateKeyPairSync,
privateEncrypt,
publicDecrypt,
Expand Down Expand Up @@ -138,6 +139,10 @@ export function createRSA(): RSAKeys {
}
}

export function derivePublicKeyFromPrivate(privateKeyPem: string): string {
return createPublicKey(privateKeyPem).export({ type: 'pkcs1', format: 'pem' }) as string
}

/**
* Calculate the key ID from a public key
* Shows the first 20 characters of base64-encoded key body for easy visual verification
Expand Down
38 changes: 27 additions & 11 deletions cli/src/bundle/encrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { parse } from '@std/semver'
import { trackEvent } from '../analytics/track'
import { encryptChecksum, encryptChecksumV3, encryptSource, generateSessionKey } from '../api/crypto'
import { checkAlerts } from '../api/update'
import { ensurePublicKeyFromPrivateKey, ensurePublicKeyInConfig } from '../recovery/public-key'
import { CliUserError } from '../shared/cli-user-error'
import { baseKeyV2, findRoot, formatError, getConfig, getInstalledVersion, isDeprecatedPluginVersion } from '../utils'
import { baseKeyV2, canPromptInteractively, findRoot, formatError, getConfigForWrite, getInstalledVersion, isDeprecatedPluginVersion } from '../utils'
import { requireChecksum, requireExistingZipPath, requireZipPath } from './validate-inputs'

export type { EncryptResult } from '../schemas/bundle'
Expand Down Expand Up @@ -48,15 +49,36 @@ export async function encryptZipInternal(
checksum = requireChecksum(checksum)
requireExistingZipPath(zipPath)

const extConfig = await getConfig()
const interactive = canPromptInteractively({ silent: json || silent })
const userSuppliedPrivateKey = options.keyData !== undefined || options.key !== undefined
const keyPath = options.key || baseKeyV2
let privateKey = options.keyData || ''
Comment thread
cursor[bot] marked this conversation as resolved.
if (!privateKey && existsSync(keyPath))
privateKey = readFileSync(keyPath, 'utf8')

const hasPrivateKeyInConfig = !!extConfig.config.plugins?.CapacitorUpdater?.privateKey
let extConfig = await getConfigForWrite()
const hasPublicKeyInConfig = !!extConfig.config.plugins?.CapacitorUpdater?.publicKey

if (!hasPublicKeyInConfig) {
if (privateKey) {
await ensurePublicKeyFromPrivateKey(privateKey, { silent: silent || json, json })
}
else if (!userSuppliedPrivateKey) {
await ensurePublicKeyInConfig({ interactive, silent: silent || json, json })
}
extConfig = await getConfigForWrite()
}
Comment thread
cursor[bot] marked this conversation as resolved.

if (!privateKey && existsSync(keyPath))
privateKey = readFileSync(keyPath, 'utf8')

const hasPrivateKeyInConfig = !!extConfig.config.plugins?.CapacitorUpdater?.privateKey
const refreshedHasPublicKey = !!extConfig.config.plugins?.CapacitorUpdater?.publicKey

if (hasPrivateKeyInConfig && shouldShowPrompts)
log.warning('There is still a privateKey in the config')

if (!hasPublicKeyInConfig) {
if (!refreshedHasPublicKey && !(userSuppliedPrivateKey && !privateKey)) {
if (!silent) {
if (json)
emitJsonError({ error: 'missing_public_key' })
Expand All @@ -66,10 +88,7 @@ export async function encryptZipInternal(
throw new Error('Missing public key in config')
}

const keyPath = options.key || baseKeyV2
let privateKey = options.keyData || ''

if (!existsSync(keyPath) && !privateKey) {
if (!privateKey) {
if (!silent) {
if (json) {
emitJsonError({ error: 'missing_key' })
Expand All @@ -81,9 +100,6 @@ export async function encryptZipInternal(
}
throw new Error('Missing private key')
}
else if (existsSync(keyPath)) {
privateKey = readFileSync(keyPath, 'utf8')
}

if (privateKey && !privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----')) {
if (!silent) {
Expand Down
Loading
Loading