latest updates
This commit is contained in:
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
@@ -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.
|
||||
Reference in New Issue
Block a user