latest updates

This commit is contained in:
Mark Gregory
2026-09-08 20:06:58 +01:00
parent fb05cf71ed
commit 868604b848
53 changed files with 6442 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
# Rsync Studio — Security / Release Audit
This project targets direct macOS distribution using Developer ID signing and Apple notarisation. App Sandbox is intentionally disabled because the product launches `rsync`/SSH and operates on arbitrary user-configured filesystem paths.
## Security controls present
- Hardened Runtime enabled for Debug and Release target configurations.
- No shell (`/bin/sh -c`) command construction; `Process` uses an absolute executable path and argument array.
- SSH host-key checking is not disabled.
- Automated SSH uses BatchMode, timeout and keepalive limits.
- SSH password authentication/storage is not supported; legacy stored password data is deleted.
- Remote host, username, port, key path and remote destination are validated.
- Remote paths reject shell metacharacters before rsync can invoke the remote shell.
- Advanced arguments reject transport replacement, remote-command, credential, arbitrary list/log/batch path and destructive overrides.
- Malformed Advanced argument quoting/trailing escapes are rejected.
- `--` terminates rsync option parsing before source/destination operands.
- Local source/destination overlap is rejected.
- File-watch and exclusion values reject control characters.
- Scheduled/watch delete requires explicit per-job authorisation.
- Run logs are ANSI-cleaned, likely credentials redacted and size-capped before persistence.
- Run history has bounded retention.
- Privacy manifest declares no tracking/no collected data and the app-specific UserDefaults required reason.
- Launch at login uses `SMAppService` and remains user-controlled.
- No analytics/telemetry or application HTTP client code is present.
## Required release verification on a Mac
Run:
```bash
./Scripts/release-preflight.sh
```
Then archive with Developer ID, submit with Apple `notarytool` (or Xcode Organizer), staple the ticket and run the signature/Gatekeeper checks in `Docs/RELEASE.md`.
A Linux source audit cannot substitute for Xcode's macOS SDK typecheck, code signing, notarisation or Gatekeeper assessment.
+88
View File
@@ -0,0 +1,88 @@
# Rsync Studio — Commercial Release Stage 1
This document freezes the commercial distribution identity and release-security baseline for Rsync Studio 1.0.0.
## Product identity
- Product: **Rsync Studio**
- Bundle identifier: **`net.absdev.RsyncStudio`**
- Marketing version: **`1.0.0`**
- Build number: **`1`**
- Minimum macOS: **14.0**
- Category: **Utilities**
- Distribution model: **Direct distribution outside the Mac App Store**
The bundle identifier is now treated as permanent product identity. Do not change it between commercial releases. Changing it creates a different application identity for macOS, Keychain access, preferences, update logic and future licensing.
## Apple signing baseline
Rsync Studio must ship with:
1. An active Apple Developer Program membership.
2. A **Developer ID Application** certificate belonging to the publisher's Apple Developer Team.
3. Hardened Runtime enabled.
4. No `get-task-allow` entitlement in a Release archive.
5. Successful Apple notarization.
6. A stapled notarization ticket before publication.
7. Successful Gatekeeper assessment of the exact artifact customers will download.
The Apple Team ID and certificate's full identity are deliberately not committed into the repository. Select the correct team in Xcode on the controlled release Mac. This avoids embedding developer-account-specific credentials into source control.
## App Sandbox
The App Sandbox remains intentionally disabled. Rsync Studio launches command-line synchronization/SSH tooling and must work with arbitrary user-selected filesystem locations. This is a direct-distribution architecture decision, not an accidental security relaxation.
Hardened Runtime remains mandatory.
## Versioning policy
Use semantic product versions:
- `1.0.0`, `1.0.1`, `1.1.0`, `2.0.0`
Increment `CURRENT_PROJECT_VERSION` for every build that may be distributed. Never publish two different binaries using the same version/build pair.
## Required Stage 1 checks
Run:
```bash
./Scripts/verify-release-config.sh
./Scripts/security-check.sh
./Scripts/release-preflight.sh
```
`release-preflight.sh` must be run on macOS with Xcode installed. It performs a clean Release build without signing so compiler/SDK failures are caught before creating the signed archive.
## First signed archive checklist
On the release Mac:
1. Open `Rsync Studio.xcodeproj` in Xcode.
2. Select the **Rsync Studio** target → **Signing & Capabilities**.
3. Select the publisher's Apple Developer Team.
4. Confirm Hardened Runtime is enabled.
5. Archive a **Release** build for macOS.
6. In Organizer choose direct/Developer ID distribution.
7. Confirm the archive is signed with **Developer ID Application**, not Apple Development.
8. Notarize the exported app/container with Apple.
9. Staple the ticket.
10. Verify the exact final artifact before uploading it anywhere.
## Signed artifact verification
For the exported application:
```bash
codesign --verify --deep --strict --verbose=2 "/path/to/Rsync Studio.app"
codesign -dv --verbose=4 "/path/to/Rsync Studio.app"
codesign -d --entitlements :- "/path/to/Rsync Studio.app"
spctl --assess --type execute --verbose=4 "/path/to/Rsync Studio.app"
xcrun stapler validate "/path/to/Rsync Studio.app"
```
The signing output must identify a Developer ID Application certificate belonging to the intended publisher. The entitlements must not contain `com.apple.security.get-task-allow`.
## Stage boundary
Stage 1 does **not** add customer licensing, payment handling, trial enforcement, DMG artwork, automatic installation of updates, or a licensing server. Those are intentionally separate stages so signing/distribution identity is stable before commercial logic is introduced.
+104
View File
@@ -0,0 +1,104 @@
# Rsync Studio — Commercial Release Stage 2
Stage 2 adds a reproducible, non-publishing direct-distribution release pipeline for macOS.
## What the pipeline does
`Scripts/release.sh` performs these steps in order:
1. Runs the existing release/security preflight.
2. Creates a clean Xcode Release archive.
3. Exports the archive using Apple's `developer-id` distribution method.
4. Verifies that the exported application is signed by **Developer ID Application** and has Hardened Runtime enabled.
5. Rejects `get-task-allow` and an unexpectedly sandboxed Release app.
6. Builds a compressed drag-to-Applications DMG using `hdiutil`.
7. Validates the configured `notarytool` Keychain profile.
8. Notarizes the exported app inside a temporary ZIP, staples its ticket directly to the app, validates it, then removes the temporary ZIP.
9. Builds the final DMG from that already-notarized/stapled app and validates the DMG container.
10. Submits the final DMG to Apple's notarization service and waits for the result.
11. Staples and validates the DMG notarization ticket.
12. Re-runs Gatekeeper/signature checks after notarization.
13. Produces a SHA-256 checksum and release metadata file.
14. Stops without uploading or publishing anything.
Existing release directories are immutable: the script refuses to overwrite `build/releases/<version>-<build>`. Increment the build number for a replacement build.
## One-time Mac setup
Install/sign into the current Xcode release and make sure a **Developer ID Application** certificate for your Apple Developer team is available in Keychain Access/Xcode.
Store notarization credentials once:
```bash
xcrun notarytool store-credentials "Rsync Studio Notary"
```
Apple will prompt for the required account/team credentials. The release script never stores those credentials in the source repository.
If Xcode cannot infer your team, run releases with your 10-character Apple Developer Team ID:
```bash
DEVELOPMENT_TEAM=YOURTEAMID ./Scripts/release.sh
```
Do not commit the Team ID solely because a local machine needs it; Xcode signing configuration can supply it.
## Produce a commercial build
From the repository root on the release Mac:
```bash
./Scripts/release.sh
```
To use another notarization Keychain profile:
```bash
./Scripts/release.sh --notary-profile MyNotaryProfile
```
For a signing/package smoke test without Apple notarization:
```bash
./Scripts/release.sh --skip-notarization
```
A skipped-notarization build is **not** suitable for customer distribution.
## Output
For version `1.0.0`, build `1`, the output is:
```text
build/releases/1.0.0-1/
├── Rsync Studio.xcarchive
├── export/
│ └── Rsync Studio.app
├── Rsync Studio-1.0.0.dmg
├── Rsync Studio-1.0.0.dmg.sha256
└── release-info.txt
```
The release directory deliberately includes the archive and exported application so a shipped artifact can be traced back to the exact signed archive.
## Mandatory clean-Mac test before publishing
Do not publish immediately after the script succeeds. Copy or download the final DMG onto a Mac/account that has not built Rsync Studio in Xcode, then:
1. Open the DMG.
2. Drag **Rsync Studio** into Applications.
3. Launch it from Applications.
4. Confirm Gatekeeper opens it normally with no unidentified-developer warning.
5. Create a harmless local dry-run job.
6. Verify SSH/local workflows required for the release.
7. Verify About reports the intended version/build.
8. Verify the app icon, Help menu, notifications and Launch at Login behaviour.
9. Eject/delete the DMG and repeat once from the actual hosted download before announcing the release.
## Release artifact policy
Never replace `Rsync Studio-1.0.0.dmg` with different bytes after it has been published. If the build changes, increment `CURRENT_PROJECT_VERSION` and create a new release artifact. Keep the published SHA-256 alongside the download.
## What Stage 2 intentionally does not do
It does not upload to a website/CDN, update the signed update manifest, publish release notes, create licences, take payments or activate customers. Those operations are deliberately separated so a successful local build cannot accidentally become a public release.
+114
View File
@@ -0,0 +1,114 @@
# Rsync Studio — Direct Distribution Release
Rsync Studio is designed for direct macOS distribution rather than the Mac App Store because it intentionally launches `rsync`/`ssh` and accesses user-selected arbitrary paths.
## Release security baseline
- Developer ID Application signing.
- Hardened Runtime enabled.
- App Sandbox intentionally disabled for this product architecture.
- No Hardened Runtime exception entitlements.
- SSH host-key verification is never disabled.
- SSH uses BatchMode, connection timeout and keepalive limits.
- Password storage/authentication is not supported; use a private key or the macOS SSH agent.
- Advanced rsync arguments that can replace the SSH transport, run remote commands, load passwords, read/write arbitrary option/list/log/batch files or bypass controlled delete handling are rejected.
- Scheduled/file-watch `--delete` requires explicit per-job authorisation.
- Run logs are sanitised and capped before storage and expire according to Settings.
- `PrivacyInfo.xcprivacy` declares no tracking/no collected data and the UserDefaults required-reason API.
## Mandatory preflight
Before every release archive, run on the release Mac:
```bash
./Scripts/release-preflight.sh
```
This performs the static security policy checks, validates the privacy manifest, and performs a clean unsigned **Release** build with the installed macOS SDK so compiler/typecheck warnings or SDK incompatibilities are caught before signing.
## Archive
In Xcode select **Any Mac (Apple Silicon, Intel)**, choose **Product > Archive**, then distribute using **Developer ID**.
Alternatively:
```bash
xcodebuild -project "Rsync Studio.xcodeproj" -scheme "Rsync Studio" -configuration Release -archivePath "build/Rsync Studio.xcarchive" archive
```
Choose your Developer ID Application certificate/team in Xcode signing settings before archiving.
## Verify signature
```bash
codesign --verify --deep --strict --verbose=2 "/path/to/Rsync Studio.app"
codesign -d --entitlements :- "/path/to/Rsync Studio.app"
```
The Release build should show Hardened Runtime and should not contain `com.apple.security.get-task-allow`.
## Notarise
Store notarisation credentials in a Keychain profile once:
```bash
xcrun notarytool store-credentials "Rsync Studio Notary"
```
Submit a signed ZIP/DMG/PKG:
```bash
ditto -c -k --keepParent "/path/to/Rsync Studio.app" "Rsync Studio.zip"
xcrun notarytool submit "Rsync Studio.zip" --keychain-profile "Rsync Studio Notary" --wait
```
Staple and validate:
```bash
xcrun stapler staple "/path/to/Rsync Studio.app"
xcrun stapler validate "/path/to/Rsync Studio.app"
spctl --assess --type execute --verbose=4 "/path/to/Rsync Studio.app"
```
## Pre-release functional security tests
1. Local source nested inside destination is rejected.
2. Local destination nested inside source is rejected.
3. SSH host beginning with `-` is rejected.
4. SSH username containing spaces/control characters is rejected.
5. A `.pub` key cannot be selected as the effective private key.
6. Advanced arguments `-e`, `--rsh`, `--rsync-path`, `--password-file`, `--files-from`, `--log-file`, `--write-batch`, `--remove-source-files`, `--delete*` and `-M` are rejected, and malformed quoting is rejected.
7. Remote destination shell metacharacters are rejected before rsync is launched.
8. Scheduled/watch job using `--delete` cannot run until **Allow unattended delete** is explicitly enabled.
9. SSH to a previously unknown host does not silently bypass host-key checking.
10. Scheduled SSH job with unavailable authentication fails rather than prompting indefinitely.
11. History expiry removes old completed runs according to Settings.
12. A failed persistent SwiftData store opens Recovery Mode instead of immediately crashing.
13. `Scripts/security-check.sh` passes before every signed build.
## Signed update feed
The application contains a dependency-free signed update checker. It remains disabled until these generated Info.plist values are populated in the Release configuration:
- `RsyncStudioUpdateManifestURL`: HTTPS URL to the JSON manifest.
- `RsyncStudioUpdatePublicKey`: Base64 raw Ed25519/Curve25519.Signing public key.
Manifest schema:
```json
{
"version": "1.2.0",
"build": 120,
"downloadURL": "https://example.com/releases/Rsync Studio-1.2.0.dmg",
"signature": "BASE64_ED25519_SIGNATURE"
}
```
The exact signed bytes are UTF-8: `version + "\n" + build + "\n" + downloadURL`. The checker refuses HTTP feeds, unsigned manifests, invalid signatures, and non-HTTPS download URLs. It opens the verified signed download location; it does not silently install software. Keep the private signing key outside the source repository and build machine where practical.
## Automated commercial release pipeline
Stage 2 adds `Scripts/release.sh`, which automates archive/export, signed-app verification, drag-to-Applications DMG creation, Apple notarization, stapling, Gatekeeper verification, SHA-256 generation and release metadata. It intentionally does **not** publish artifacts.
See `Docs/COMMERCIAL_RELEASE_STAGE2.md` for first-time setup and usage.