Skip to content
Draft
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
38 changes: 38 additions & 0 deletions .env.desktop.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
APP_NAME=Vito
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://127.0.0.1

VITO_DESKTOP=true
VITO_DATA_PATH=
VITO_ENV_PATH=
VITO_STORAGE_PATH=

APP_SERVICES_CACHE=
APP_PACKAGES_CACHE=
APP_CONFIG_CACHE=
APP_ROUTES_CACHE=
APP_EVENTS_CACHE=
VIEW_COMPILED_PATH=

DB_CONNECTION=sqlite
DB_DATABASE=database.sqlite

QUEUE_CONNECTION=database
DB_QUEUE=default
QUEUE_FAILED_DRIVER=database-uuids

CACHE_DRIVER=file
SESSION_DRIVER=file
FILESYSTEM_DISK=local
INERTIA_ENSURE_PAGES_EXIST=false

MAIL_MAILER=log

WS_HOST=127.0.0.1
WS_PORT=8085
WS_ALLOWED_ORIGINS=
WS_BROADCAST_SECRET=

SCRIBE_AUTH_KEY="YOUR-API-KEY"
86 changes: 86 additions & 0 deletions .github/workflows/desktop-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: desktop-ci

on:
pull_request:
paths:
- 'desktop/**'
- 'scripts/desktop/**'
- '.github/workflows/desktop-ci.yml'
- '.github/workflows/desktop-release.yml'
- 'vite.config.ts'
- 'bootstrap/app.php'
- 'config/desktop.php'
- 'app/Support/DesktopRuntime.php'
Comment on lines +5 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Run desktop CI for every desktop integration surface.

These filters omit supplied stack dependencies such as app/Actions/Desktop/**, desktop controllers and middleware, desktop authentication pages, and frontend types. Changes there can break the packaged application without running this workflow. Add the complete desktop integration paths, or use broader app/**, config/**, and resources/** filters.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 3-13: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/desktop-ci.yml around lines 5 - 13, Expand the path
filters in the desktop CI workflow so changes to all desktop integration
surfaces trigger it, including app actions, controllers, middleware,
authentication pages, and frontend types. Prefer broader app/**, config/**, and
resources/** filters, while preserving the existing desktop-specific workflow
and configuration paths.


jobs:
typecheck:
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin workflow actions to immutable commit SHAs.

The new packaging workflows depend on mutable action tags, allowing an upstream tag change to execute different code in privileged build and release jobs.

  • .github/workflows/desktop-ci.yml#L20-L20: pin actions/checkout.
  • .github/workflows/desktop-ci.yml#L23-L23: pin actions/setup-node.
  • .github/workflows/desktop-ci.yml#L39-L39: pin actions/checkout.
  • .github/workflows/desktop-ci.yml#L42-L42: pin shivammathur/setup-php.
  • .github/workflows/desktop-ci.yml#L51-L51: pin actions/setup-node.
  • .github/workflows/desktop-ci.yml#L65-L65: pin actions/cache.
  • .github/workflows/desktop-release.yml#L73-L73: pin actions/checkout.
  • .github/workflows/desktop-release.yml#L78-L78: pin shivammathur/setup-php.
  • .github/workflows/desktop-release.yml#L87-L87: pin actions/setup-node.
  • .github/workflows/desktop-release.yml#L102-L102: pin actions/cache.
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 20-20: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

📍 Affects 2 files
  • .github/workflows/desktop-ci.yml#L20-L20 (this comment)
  • .github/workflows/desktop-ci.yml#L23-L23
  • .github/workflows/desktop-ci.yml#L39-L39
  • .github/workflows/desktop-ci.yml#L42-L42
  • .github/workflows/desktop-ci.yml#L51-L51
  • .github/workflows/desktop-ci.yml#L65-L65
  • .github/workflows/desktop-release.yml#L73-L73
  • .github/workflows/desktop-release.yml#L78-L78
  • .github/workflows/desktop-release.yml#L87-L87
  • .github/workflows/desktop-release.yml#L102-L102
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/desktop-ci.yml at line 20, Pin every referenced workflow
action to a full immutable commit SHA instead of a mutable tag:
actions/checkout, actions/setup-node, shivammathur/setup-php, and actions/cache
at .github/workflows/desktop-ci.yml lines 20, 23, 39, 42, 51, and 65, and
.github/workflows/desktop-release.yml lines 73, 78, 87, and 102.

Source: Linters/SAST tools


- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.x'

- name: Install desktop shell dependencies
run: npm ci
working-directory: desktop

- name: Build desktop shell
run: npm run build
working-directory: desktop

package-smoke:
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: ctype, curl, dom, fileinfo, filter, ftp, iconv, intl, mbstring, openssl, pdo_sqlite, sqlite3, zip

- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress --no-dev --optimize-autoloader

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.x'

- name: Install NPM dependencies
run: npm install --force

- name: Build desktop frontend assets
run: npm run build:desktop

- name: Assemble desktop app distribution
run: bash scripts/desktop/build-app.sh

- name: Cache PHP runtime
uses: actions/cache@v4
with:
path: desktop/resources/bin
key: desktop-php-linux-x64-${{ hashFiles('scripts/desktop/fetch-php.sh') }}

- name: Fetch PHP runtime
run: bash scripts/desktop/fetch-php.sh linux x64

- name: Smoke test desktop app distribution
run: bash scripts/desktop/smoke-test.sh "$PWD/desktop/resources/bin/linux/x64/php"

- name: Install desktop shell dependencies
run: npm ci
working-directory: desktop

- name: Build desktop shell
run: npm run build
working-directory: desktop

- name: Package desktop app (unpacked)
run: npx electron-builder --config electron-builder.yml --linux --x64 --dir --publish never
working-directory: desktop
161 changes: 161 additions & 0 deletions .github/workflows/desktop-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
name: desktop-release

on:
workflow_dispatch:
inputs:
version:
description: 'Existing release tag to build and attach desktop apps to (e.g. 4.1.0)'
required: true
workflow_call:
inputs:
version:
description: 'Existing release tag to build and attach desktop apps to'
required: true
type: string
secrets:
DESKTOP_MAC_CSC_LINK:
required: false
DESKTOP_MAC_CSC_KEY_PASSWORD:
required: false
DESKTOP_WIN_CSC_LINK:
required: false
DESKTOP_WIN_CSC_KEY_PASSWORD:
required: false
DESKTOP_APPLE_ID:
required: false
DESKTOP_APPLE_APP_SPECIFIC_PASSWORD:
required: false
DESKTOP_APPLE_TEAM_ID:
required: false

permissions:
contents: write

jobs:
build:
name: Build ${{ matrix.name }}
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
include:
- name: macos-arm64
os: macos-15
platform: darwin
arch: arm64
build-args: --mac --arm64
- name: macos-x64
os: macos-15-intel
platform: darwin
arch: x64
build-args: --mac --x64
- name: windows-x64
os: windows-latest
platform: win32
arch: x64
build-args: --win --x64
- name: linux-x64
os: ubuntu-24.04
platform: linux
arch: x64
build-args: --linux --x64

steps:
- name: Remove optional "v" prefix
id: version
shell: bash
run: echo "version=${VERSION#v}" >> "$GITHUB_OUTPUT"
env:
VERSION: ${{ inputs.version }}

- name: Checkout release tag
uses: actions/checkout@v4
with:
ref: ${{ steps.version.outputs.version }}

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: ctype, curl, dom, fileinfo, filter, ftp, iconv, intl, mbstring, openssl, pdo_sqlite, sqlite3, zip

- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress --no-dev --optimize-autoloader

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.x'

- name: Install NPM dependencies
run: npm install --force

- name: Build desktop frontend assets
run: npm run build:desktop

- name: Assemble desktop app distribution
shell: bash
run: bash scripts/desktop/build-app.sh

- name: Cache PHP runtime
uses: actions/cache@v4
with:
path: desktop/resources/bin
key: desktop-php-${{ matrix.platform }}-${{ matrix.arch }}-${{ hashFiles('scripts/desktop/fetch-php.sh') }}

- name: Fetch PHP runtime
shell: bash
run: bash scripts/desktop/fetch-php.sh "${{ matrix.platform }}" "${{ matrix.arch }}"

- name: Smoke test desktop app distribution
shell: bash
run: |
PHP_BIN="desktop/resources/bin/${{ matrix.platform }}/${{ matrix.arch }}/php"
if [ "${{ matrix.platform }}" = "win32" ]; then
PHP_BIN="$PHP_BIN.exe"
fi
bash scripts/desktop/smoke-test.sh "$PWD/$PHP_BIN"

- name: Install desktop shell dependencies
shell: bash
run: npm ci
working-directory: desktop

- name: Build desktop shell
shell: bash
run: npm run build
working-directory: desktop

- name: Set desktop app version
shell: bash
run: npm version "${{ steps.version.outputs.version }}" --no-git-tag-version --allow-same-version
working-directory: desktop

- name: Package desktop app
shell: bash
run: npx electron-builder --config electron-builder.yml ${{ matrix.build-args }} --publish never
working-directory: desktop
env:
CSC_LINK: ${{ secrets.DESKTOP_MAC_CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.DESKTOP_MAC_CSC_KEY_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.DESKTOP_WIN_CSC_LINK }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.DESKTOP_WIN_CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.DESKTOP_APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.DESKTOP_APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.DESKTOP_APPLE_TEAM_ID }}
CSC_IDENTITY_AUTO_DISCOVERY: ${{ secrets.DESKTOP_MAC_CSC_LINK != '' }}
Comment on lines +135 to +147

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Expose signing credentials only to their target platform.

Every matrix job currently receives the macOS, Windows, and Apple notarisation credentials. A compromised Linux or Windows packaging dependency could therefore exfiltrate unrelated signing identities. Export macOS secrets only for darwin and Windows secrets only for win32, preferably through separately protected jobs or environments.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 137-137: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/desktop-release.yml around lines 135 - 147, Update the
“Package desktop app” step and its matrix-specific environment handling so macOS
signing/notarization secrets are exposed only when the target platform is
darwin, while Windows signing secrets are exposed only for win32. Keep unrelated
credentials unset for Linux and other platforms, using platform-specific jobs or
protected environments rather than exporting all secrets to every matrix job.


- name: Upload desktop apps to release
shell: bash
run: |
shopt -s nullglob
FILES=(desktop/release/*.dmg desktop/release/*-mac-*.zip desktop/release/*.exe desktop/release/*.AppImage)
if [ ${#FILES[@]} -eq 0 ]; then
echo "No desktop artifacts found to upload" >&2
exit 1
fi
gh release upload "$VERSION" "${FILES[@]}" --clobber
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
16 changes: 16 additions & 0 deletions .github/workflows/releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,22 @@ jobs:
echo "Discord notification failed; release succeeded regardless"
fi

desktop:
needs: release
permissions:
contents: write
uses: ./.github/workflows/desktop-release.yml
with:
version: ${{ needs.release.outputs.version }}
secrets:
DESKTOP_MAC_CSC_LINK: ${{ secrets.DESKTOP_MAC_CSC_LINK }}
DESKTOP_MAC_CSC_KEY_PASSWORD: ${{ secrets.DESKTOP_MAC_CSC_KEY_PASSWORD }}
DESKTOP_WIN_CSC_LINK: ${{ secrets.DESKTOP_WIN_CSC_LINK }}
DESKTOP_WIN_CSC_KEY_PASSWORD: ${{ secrets.DESKTOP_WIN_CSC_KEY_PASSWORD }}
DESKTOP_APPLE_ID: ${{ secrets.DESKTOP_APPLE_ID }}
DESKTOP_APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.DESKTOP_APPLE_APP_SPECIFIC_PASSWORD }}
DESKTOP_APPLE_TEAM_ID: ${{ secrets.DESKTOP_APPLE_TEAM_ID }}

docker-tags:
runs-on: ubuntu-24.04
needs: release
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/.phpunit.cache
/node_modules
/desktop/node_modules
/desktop/dist
/desktop/release
/desktop/resources/bin
/desktop-build
/public/build-desktop
/public/hot
/public/storage
/storage/*.key
Expand Down
35 changes: 35 additions & 0 deletions app/Actions/Desktop/SetupDesktopAdmin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Actions\Desktop;

use App\Actions\User\CreateUser;
use App\Enums\UserRole;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;

class SetupDesktopAdmin
{
/**
* @param array<string, mixed> $input
*/
public function create(array $input): User
{
Validator::make($input, [
'password' => ['required', 'string', 'confirmed'],
])->validate();

return DB::transaction(function () use ($input): User {
$user = app(CreateUser::class)->create([
'name' => $input['name'] ?? null,
'email' => $input['email'] ?? null,
'password' => $input['password'] ?? null,
'role' => UserRole::ADMIN->value,
]);

$user->ensureHasDefaultProject();

return $user;
});
Comment on lines +22 to +33

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

First-run administrator eligibility is not enforced atomically. Concurrent unauthenticated requests can both observe an empty user table before creating separate administrators.

  • app/Actions/Desktop/SetupDesktopAdmin.php#L22-L33: serialise setup and re-check that no user exists inside the protected operation.
  • app/Http/Controllers/DesktopAuthController.php#L44-L46: remove this check as the authoritative guard, or retain it only as an early optimisation.
🧰 Tools
🪛 PHPMD (2.15.0)

[error] 22-33: Avoid using static access to class '\Illuminate\Support\Facades\DB' in method 'create'. (undefined)

(StaticAccess)

📍 Affects 2 files
  • app/Actions/Desktop/SetupDesktopAdmin.php#L22-L33 (this comment)
  • app/Http/Controllers/DesktopAuthController.php#L44-L46
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Actions/Desktop/SetupDesktopAdmin.php` around lines 22 - 33, Make
SetupDesktopAdmin’s transaction the authoritative first-run guard: serialize the
setup operation and re-check that no users exist within the protected operation
before creating the administrator. In app/Actions/Desktop/SetupDesktopAdmin.php
lines 22-33, update the flow around the transaction and CreateUser call; in
app/Http/Controllers/DesktopAuthController.php lines 44-46, remove the existing
user-existence check or retain it only as a non-authoritative early
optimization.

}
}
Loading
Loading