Skip to content

Fix pre-testing review findings: legal attribution, security hardening, UX bugs#720

Closed
Alfredoalv13 wants to merge 32 commits into
webadderallorg:mainfrom
Alfredoalv13:fix/mvp-pretest-review-findings
Closed

Fix pre-testing review findings: legal attribution, security hardening, UX bugs#720
Alfredoalv13 wants to merge 32 commits into
webadderallorg:mainfrom
Alfredoalv13:fix/mvp-pretest-review-findings

Conversation

@Alfredoalv13

@Alfredoalv13 Alfredoalv13 commented Jul 2, 2026

Copy link
Copy Markdown

Summary

Addresses every item from the pre-MVP-testing review, in priority order, via a 10-phase fix→verify→repair workflow (each phase re-ran tsc --noEmit + the full test suite before moving on) plus an adversarial security-diff review at the end.

Legal / branding

  • Added an in-app About dialog with the AGPLv3-required Recordly/OpenScreen attribution (previously missing entirely from the UI)
  • Fixed the broken ./LICENSE link in README.md, rebranded README.zh-CN.md (was still 100% unbranded Recordly content), updated CONTRIBUTING.md repo references, added license field to package.json

Critical security

  • Fixed an RCE: generate-auto-captions accepted an arbitrary renderer-supplied whisperExecutablePath and executed it — now gated by an approved-path allowlist (bundled binary or a path that actually came from the file-picker dialog)
  • Enabled webSecurity and added a real CSP on all windows (was webSecurity: false app-wide with no CSP)
  • Disabled remote/marketplace extension installs for the testing phase (extension code runs fully unsandboxed in the renderer); hardened local folder-install trust; namespaced extension localStorage settings so extensions can't read each other's data
  • Fixed a data-loss race: app quit no longer bypasses in-flight autosave
  • Closed arbitrary file read/write gaps in write-exported-video-to-path, open-project-file-at-path, read-local-file, and generate-wallpaper-thumbnail — all now confined to app-managed directories, consistent with the codebase's existing validated handlers

Build pipeline

  • whisper.cpp source tarball download now verified against a pinned SHA-256 before build
  • Windows release build now hard-fails (like macOS already did) if signing secrets are missing, instead of silently shipping unsigned
  • Native-helper manifest hash mismatches are now fatal instead of warn-only
  • Homebrew tap auto-merge now defaults off

UX / reliability

  • GIF export no longer hangs forever on encoder error
  • Orphaned export temp-file cleanup failures are now logged instead of silent
  • Native/NVIDIA export fallback to WebCodecs now shows a toast instead of failing silently
  • "Open Video" picker failure now shows an error instead of doing nothing
  • Sanitized IPC error messages returned to the renderer in high-traffic handlers (was leaking stack/path fragments)
  • Throttled caption clip-path recomputation (was uncoalesced RAF work on every layout change)

Post-workflow cleanup (found by the adversarial security-diff review):

  • Restored a native-helper-hash-enforce commit that got accidentally reverted mid-workflow by a concurrent-commit race
  • Fixed a fail-open dead-code path in the extension review-status check (getMarketplaceExtension() returning null on error was silently treated as "approved" instead of rejected)

What's intentionally NOT in this PR

  • Full extension sandboxing (iframe/worker isolation) — scoped down to "disable remote installs" for now; a real sandbox is a larger follow-up
  • Moving the update-feed trust root off a personal GitHub account, and confirming ownership of the WinGet/Homebrew package identifiers — these are organizational decisions, not code fixes
  • Committing the locally-rebuilt native helper binaries currently showing as modified in the working tree — left untouched, flagged separately

Test plan

  • npx tsc --noEmit — clean
  • npm test — 786/786 tests passing, 92/92 files
  • Adversarial security-diff review of every security-relevant commit against the base commit — one issue found and fixed (see above), everything else confirmed as genuine, correctly-scoped fixes (allowlists traced to their population sites, no dead validators, no unbounded matches)
  • Manual smoke test on a packaged build (recording → edit → export, extension loading, About dialog) — recommended before merging, since several of these touch Electron window/security config directly

🤖 Generated with a Claude Code workflow (10 phases, 47 sub-agents)

Summary by CodeRabbit

  • New Features

    • Introduced the VybeClip Studio home screen with recent projects, import/open actions, and recording controls.
    • Added an About dialog and refreshed update/install messaging across the app.
  • Bug Fixes

    • Improved handling for macOS permissions, project opening, exports, and temporary file cleanup.
    • Tightened security and reliability for recording, captions, extensions, and file access flows.
  • Documentation

    • Updated the README, contribution notes, and release guidance for the VybeClip rebrand and new macOS workflow.

Side-Quest-Studios and others added 30 commits June 16, 2026 14:04
… repo references

- README.md: fix LICENSE link to point at LICENSE.md (file was renamed)
- README.zh-CN.md: rebrand from Recordly to VybeClip, matching README.md's
  MVP-focused content and structure (title, features, dev setup, license
  and attribution section)
- CONTRIBUTING.md: point issue reporting at this fork (Alfredoalv13/Recordly)
  instead of the upstream repo
- package.json: add top-level "license": "AGPL-3.0-or-later" field
Adds a small AboutDialog component (Radix Dialog, matching the existing
FeedbackDialog/KeyboardShortcutsDialog pattern) reachable from the editor
header. Shows the app name/version, Box Creative Studio, an attribution
line for Recordly (AGPLv3, webadderall) and OpenScreen (MIT, Siddharth
Vaddem), and a link to the full LICENSE.md, satisfying the AGPLv3
user-facing attribution requirement.
CONTRIBUTING.md still told contributors their work would be licensed
under a nonexistent "./LICENSE" file under the MIT License, left over
from before the project was relicensed to AGPLv3 (LICENSE.md). The
prior Legal & Branding commit fixed this same stale link in README.md
but missed CONTRIBUTING.md, leaving the docs internally inconsistent
with the new package.json "license": "AGPL-3.0-or-later" field and the
AboutDialog's AGPLv3 attribution text. Point it at LICENSE.md/AGPLv3 to
match.
generate-auto-captions previously trusted any whisperExecutablePath
sent from the renderer, gating it only on "is this file executable"
before running it via execFileAsync — letting a compromised renderer
achieve code execution in the main process via an arbitrary planted
binary.

Add an approvedWhisperExecutablePaths set (electron/ipc/state.ts),
following the existing approvedLocalReadPaths / isTrustedProjectPath /
isOwnedExportPath allowlist pattern:
- open-whisper-executable-picker now records the user-selected path
  into the approved set (electron/ipc/register/captions.ts).
- resolveWhisperExecutablePath rejects any preferredPath that isn't
  one of the app's bundled/default whisper binaries or in the
  approved set, before it's ever passed to execFileAsync
  (electron/ipc/captions/generate.ts).
webSecurity:false was disabling same-origin policy and mixed-content
blocking on the HUD overlay, studio, and editor BrowserWindows. Git
history shows it was introduced for an early prototype that played
recorded video via raw file:// URLs; the app has since grown a proper
local media server (mediaServer.ts, serving allow-listed files over
http://127.0.0.1 with range-request support) and a packaged renderer
server (rendererServer.ts, also http://127.0.0.1), but webSecurity was
never re-tightened after that migration.

- Remove webSecurity:false from the HUD overlay, studio, and editor
  windows in electron/windows.ts (source-selector, countdown, and
  update-toast windows already ran with the secure default).
- Register a Content-Security-Policy in electron/main.ts via
  session.defaultSession.webRequest.onHeadersReceived, applied to all
  windows since they share the default session. default-src/script-src
  are scoped to 'self' plus the actual (randomly-assigned) mediaServer
  and rendererServer origins; img-src/media-src additionally allow
  data:/blob:/file: so the editor canvas, cached thumbnails, and
  user-installed extension icons (already fully-trusted local code,
  loaded via file:// in ExtensionIcon.tsx) keep working, and
  getRenderableVideoUrl()'s file:// fallback still functions if the
  media server is ever unavailable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…extension in localStorage

Extension settings were all stored under one shared plaintext localStorage
key, so any extension could read every other extension's settings via raw
window.localStorage access (extensions run via dynamic import() in the same
main world), bypassing the host API's per-extension scoping. Each extension
now gets its own namespaced key ("recordly.extension-settings.v1:<id>"), with
a one-time best-effort migration that copies existing settings from the old
shared key forward so already-installed extensions don't lose their data.
…uring testing phase

Marketplace downloads have no checksum/signature verification and installed
extension code runs with full renderer access, so gate the risk down for
external testers without redesigning the trust model:

- downloadAndInstallExtension() now returns a clean, user-facing "temporarily
  disabled during the testing phase" result instead of proceeding, gated by
  isRemoteMarketplaceInstallEnabled() (env-var opt-in, defaults to off).
  Locally bundled/first-party extensions and "install from folder" are
  unaffected.
- Added a defense-in-depth check that refuses to install any marketplace
  extension whose reviewStatus isn't "approved", independent of the gate
  above, so pending/rejected/flagged extensions can never be installed even
  if remote install is later re-enabled.
- Documented/hardened that extensions:install-from-folder only ever accepts
  the path returned by dialog.showOpenDialog() — no renderer-supplied path
  parameter exists on that handler — mirroring the whisper-executable-picker
  pattern in electron/ipc/register/captions.ts.
- Updated the ExtensionManager marketplace "Browse" UI to show a clear
  "Coming soon" / beta notice in place of the Install button and card
  action, instead of letting users hit a dead-end error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…are saved or confirmed

The before-quit handler used to let the app exit immediately, even while an
autosave (triggered as fire-and-forget in VideoEditor.tsx) was still in
flight or unsaved edits existed — so Cmd+Q could silently drop the user's
latest changes with no warning, unlike window-close which already prompts.

Extract the existing "unsaved changes" dialog + save-before-close IPC flow
(previously inline in the editor window's close handler) into a reusable
promptToSaveUnsavedChangesBeforeClosing helper, and call it from
before-quit too: if there are unsaved/in-flight changes, block the quit,
reuse the same Save & Close / Discard & Close / Cancel dialog, and wait for
the renderer's save to finish (falling back to quitting anyway after a
5s timeout so a stuck save can't trap the app open). Guards against
re-entrancy so the app.quit() retry doesn't re-prompt.
…ted locations

open-project-file-at-path called loadProjectFromPath() on a renderer-supplied
path with no directory confinement, unlike save-project-file which validates
via isTrustedProjectPath. A compromised/malicious renderer could pass any
absolute path and have its JSON contents read back to the UI.

Add isTrustedOpenableProjectPath(), which mirrors isAllowedLocalReadPath's
canonicalization approach: it only allows paths inside the managed projects
directory (getProjectsDir()) or paths already present in the recent-projects
list (which is itself only ever populated by trusted dialog-driven save/load
flows), and resolves symlinks before the final check so a symlink under a
trusted location cannot point outside of it. The IPC handler now rejects any
path that fails this check before touching the filesystem.
…allpaper-thumbnail to approved paths

resolveReadableLocalFilePath previously only checked realpath + isFile(),
letting a compromised renderer read arbitrary files on disk (SSH keys,
config files with secrets, etc) via the read-local-file and
generate-wallpaper-thumbnail IPC channels. Both call sites only ever need
the bundled asset root, the recordings/userData/temp directories, or a
path the app has already approved via a real dialog.showOpenDialog result
(approveUserPath/rememberApprovedLocalReadPath). Reuse the existing
isAllowedLocalReadPath allowlist (already used by the media server and
native exports) for both the lexical and canonicalized path, closing the
gap without changing any legitimate call site's behavior.
…pp-managed directories

The "write-exported-video-to-path" handler only path.resolve()'d the
renderer-supplied outputPath before fs.writeFile, allowing an arbitrary
absolute path (e.g. from a compromised renderer or the smokeExport URL
param used by dev tooling) to overwrite any file on disk. Sibling
handlers (finalize-exported-video, discard-exported-temp) already
require path validation before touching a path; this path has no
tracked save-dialog result to trust instead, so add an
isAllowedExportWritePath check (mirroring isAllowedLocalReadPath) that
confines writes to the temp, downloads, userData, and recordings
directories and resolves symlinks to prevent directory-confinement
bypass.
isTrustedOpenableProjectPath (added to confine open-project-file-at-path
to the managed projects dir / recent-projects list) broke the
RECORDLY_SMOKE_EXPORT dev/CI flow: main.ts launches the editor with a
project fixture path from RECORDLY_SMOKE_EXPORT_PROJECT that
intentionally lives outside those locations (e.g. a CI temp dir), so it
was being rejected as untrusted.

Trust that exact path only when RECORDLY_SMOKE_EXPORT=1, read directly
from process.env in the main process rather than from any
renderer-supplied argument, so a compromised renderer still cannot use
this to smuggle in an arbitrary path. This keeps the original
security intent (block arbitrary renderer-chosen project paths) intact
while restoring the smoke-export tooling.
Homebrew tap PR auto-merge now requires an explicit opt-in via the
HOMEBREW_TAP_AUTO_MERGE repository variable instead of defaulting to
true, so releases no longer squash-merge cask updates without human
review unless a maintainer explicitly enables it.
…g secrets are missing

The Windows build previously warned and silently shipped an unsigned .exe
when WINDOWS_SIGNING_CERTIFICATE_P12_BASE64/PASSWORD were missing. Add a
dedicated "Validate Windows signing secrets" step that hard-fails (exit 1)
before signing configuration runs, mirroring the macOS job's
"Validate macOS signing secrets" step.
…fore extraction

Pin the SHA-256 of the whisper.cpp v1.8.4 source tarball and verify the
downloaded archive against it before extracting/compiling. Previously
build-whisper-runtime.mjs downloaded the tarball via plain https.get and
extracted it with no integrity check, so a compromised CDN, MITM, or a
moved/re-tagged GitHub release could silently feed malicious source into
a binary this app ships to users. The build now aborts with a clear error
on checksum mismatch, and removes the untrusted archive so it can't be
reused by a later cached run.
…ommitted

These files were mid-edit by a concurrent process and got swept into the
previous commit's index by mistake. Reverting them to their pre-commit
state here; the in-progress edits are restored as unstaged working-tree
changes so that process can continue and commit them separately.
…picker fails

The Open Video action returned silently on result.success === false,
leaving the user with no feedback on disk/permission errors. Now shows
a sonner error toast in that case.
…rever

The finalize promise had an unused _reject param and no listener for
gif.js's "error"/"abort" events, so an encoder failure left the export
promise pending indefinitely. Now both events reject with a descriptive
Error so failures surface to the caller instead of silently hanging.
…s instead of silently orphaning temp files

Extract the fire-and-forget discardExportedTemp call into a shared
discardExportedTempWithWarning helper that console.warns with the temp
file path whenever the IPC call rejects or resolves with success:false,
so a stuck multi-GB export temp file is discoverable instead of
vanishing without a trace. Applied at both call sites (cancelled export
cleanup and component unmount cleanup).
…ated export falls back to WebCodecs

When the native/NVIDIA static-layout export path throws at runtime, the
exporter silently swapped in a generic skip reason and fell back to the
WebCodecs path without telling the user their faster hardware-accelerated
export failed. Surface a non-blocking sonner toast.warning() so the user
knows why the export may be slower, while still letting the fallback
proceed as before.
Skip scheduling a new RAF while one is already pending, and skip
recomputing/writing the squircle clip-path when the caption box
geometry (width/height/radius) hasn't actually changed, to reduce
redundant work during rapid caption-layout updates (e.g. active
caption editing) without altering rendered output.
…c handlers

Several IPC handlers in assets.ts, captions.ts, export.ts, project.ts, and
settings.ts returned raw String(error)/error.message to the renderer, which
can include stack fragments and local file paths. Log the full error via
console.error server-side and return a generic, safe message instead for
the highest-traffic handlers (asset/local file reads, whisper model
download/delete, native video export session start/probe/capabilities,
folder reveal/open/set, and settings persistence).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r existing entries

Restores changes that were accidentally reverted by d0c923a during the
review workflow. A hash mismatch against a recorded manifest entry now
aborts the build (exit 1) instead of only warning, so a tampered/mismatched
native helper binary can't silently ship in a packaged build. A missing
manifest/entry (first build) still only warns, as before.
…e verified

getMarketplaceExtension() already swallows its own errors and returns null,
making the surrounding try/catch dead code. A null listing (network error,
deleted extension, non-200) was silently treated as "review status OK" and
fell through to install. Now a null listing is rejected explicitly, so the
defense-in-depth check actually fails closed instead of fail-open.

Found by the adversarial security-diff review at the end of the pretest
fix workflow.
@github-actions github-actions Bot added the Slop label Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

⚠️ This pull request has been flagged by Anti-Slop.
Our automated checks detected patterns commonly associated with
low-quality or automated/AI submissions (failure count reached).
No automatic closure — a maintainer will review it.
If this is legitimate work, please add more context, link issues, or ping us.

@Alfredoalv13

Copy link
Copy Markdown
Author

Opened against the wrong repo by mistake (gh defaulted to the upstream repo instead of my fork). Closing — the correct PR is on my fork, Alfredoalv13/Recordly. Apologies for the noise.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e05feb1-7f45-4b90-9baf-f36e08ea4beb

📥 Commits

Reviewing files that changed from the base of the PR and between 0726a08 and 87a9f6c.

⛔ Files ignored due to path filters (20)
  • icons/icons/png/1024x1024.png is excluded by !**/*.png
  • icons/icons/png/128x128.png is excluded by !**/*.png
  • icons/icons/png/16x16.png is excluded by !**/*.png
  • icons/icons/png/24x24.png is excluded by !**/*.png
  • icons/icons/png/256x256.png is excluded by !**/*.png
  • icons/icons/png/32x32.png is excluded by !**/*.png
  • icons/icons/png/48x48.png is excluded by !**/*.png
  • icons/icons/png/512x512.png is excluded by !**/*.png
  • icons/icons/png/64x64.png is excluded by !**/*.png
  • icons/icons/win/icon.ico is excluded by !**/*.ico
  • icons/vybeclip-icon-1024.png is excluded by !**/*.png
  • icons/vybeclip-icon.svg is excluded by !**/*.svg
  • package-lock.json is excluded by !**/package-lock.json
  • public/app-icons/vybeclip-1024.png is excluded by !**/*.png
  • public/app-icons/vybeclip-128.png is excluded by !**/*.png
  • public/app-icons/vybeclip-16.png is excluded by !**/*.png
  • public/app-icons/vybeclip-256.png is excluded by !**/*.png
  • public/app-icons/vybeclip-32.png is excluded by !**/*.png
  • public/app-icons/vybeclip-512.png is excluded by !**/*.png
  • public/app-icons/vybeclip-64.png is excluded by !**/*.png
📒 Files selected for processing (100)
  • .github/workflows/homebrew-tap.yml
  • .github/workflows/release.yml
  • CONTRIBUTING.md
  • README.md
  • README.zh-CN.md
  • _documentation/MVP_RELEASE.md
  • _documentation/vybeclip_studio_mockup.html
  • electron-builder.json5
  • electron/appPaths.ts
  • electron/electron-env.d.ts
  • electron/extensions/extensionIpc.ts
  • electron/extensions/extensionMarketplace.ts
  • electron/ipc/captions/generate.ts
  • electron/ipc/constants.ts
  • electron/ipc/paths/binaries.ts
  • electron/ipc/project/manager.test.ts
  • electron/ipc/project/manager.ts
  • electron/ipc/register/assets.ts
  • electron/ipc/register/captions.ts
  • electron/ipc/register/export.ts
  • electron/ipc/register/project.ts
  • electron/ipc/register/recording.ts
  • electron/ipc/register/settings.ts
  • electron/ipc/register/sources.ts
  • electron/ipc/state.ts
  • electron/ipc/utils.ts
  • electron/main.ts
  • electron/preload.ts
  • electron/updater.ts
  • electron/windows.ts
  • icons/icons/mac/icon.icns
  • package.json
  • scripts/build-cursor-monitor.mjs
  • scripts/build-nvidia-cuda-compositor.mjs
  • scripts/build-whisper-runtime.mjs
  • scripts/build-windows-capture.mjs
  • scripts/native-helper-manifest.mjs
  • scripts/sign-macos-app.mjs
  • src/App.tsx
  • src/components/brand/VybeClipLogo.tsx
  • src/components/launch/LaunchWindow.module.css
  • src/components/launch/RecordingControls.tsx
  • src/components/launch/SourceSelector.module.css
  • src/components/launch/UpdateToastWindow.tsx
  • src/components/launch/hooks/useLaunchWindowActions.ts
  • src/components/launch/launchTheme.css
  • src/components/video-editor/ExtensionManager.tsx
  • src/components/video-editor/TutorialHelp.tsx
  • src/components/video-editor/VideoEditor.tsx
  • src/components/video-editor/VideoPlayback.tsx
  • src/components/video-editor/videoPlayback/zoomRegionUtils.ts
  • src/components/vybe/VybeStudioPreview.tsx
  • src/components/vybe/studioActions.test.ts
  • src/components/vybe/studioActions.ts
  • src/hooks/useScreenRecorder.ts
  • src/i18n/locales/en/common.json
  • src/i18n/locales/en/editor.json
  • src/i18n/locales/en/extensions.json
  • src/i18n/locales/en/launch.json
  • src/i18n/locales/en/settings.json
  • src/i18n/locales/es/common.json
  • src/i18n/locales/es/editor.json
  • src/i18n/locales/es/launch.json
  • src/i18n/locales/es/settings.json
  • src/i18n/locales/fr/common.json
  • src/i18n/locales/fr/editor.json
  • src/i18n/locales/fr/launch.json
  • src/i18n/locales/fr/settings.json
  • src/i18n/locales/it/common.json
  • src/i18n/locales/it/editor.json
  • src/i18n/locales/it/launch.json
  • src/i18n/locales/it/settings.json
  • src/i18n/locales/ko/common.json
  • src/i18n/locales/ko/editor.json
  • src/i18n/locales/ko/launch.json
  • src/i18n/locales/ko/settings.json
  • src/i18n/locales/nl/common.json
  • src/i18n/locales/nl/editor.json
  • src/i18n/locales/nl/launch.json
  • src/i18n/locales/nl/settings.json
  • src/i18n/locales/pt-BR/common.json
  • src/i18n/locales/pt-BR/editor.json
  • src/i18n/locales/pt-BR/launch.json
  • src/i18n/locales/pt-BR/settings.json
  • src/i18n/locales/ru/common.json
  • src/i18n/locales/ru/launch.json
  • src/i18n/locales/ru/settings.json
  • src/i18n/locales/zh-CN/common.json
  • src/i18n/locales/zh-CN/editor.json
  • src/i18n/locales/zh-CN/launch.json
  • src/i18n/locales/zh-CN/settings.json
  • src/i18n/locales/zh-TW/common.json
  • src/i18n/locales/zh-TW/editor.json
  • src/i18n/locales/zh-TW/launch.json
  • src/i18n/locales/zh-TW/settings.json
  • src/index.css
  • src/lib/exporter/exportSavePolicy.ts
  • src/lib/exporter/gifExporter.ts
  • src/lib/exporter/modernVideoExporter.ts
  • src/lib/extensions/extensionHost.ts

📝 Walkthrough

Walkthrough

This PR rebrands the application from Recordly to VybeClip across build configuration, documentation, and all locale files, migrates the project file extension to .vybeclip with legacy support, adds allowlist-based security hardening (Whisper executables, project paths, asset reads, export writes, marketplace installs), introduces a new VybeClip Studio window/preview feature, and applies various editor theming and export/playback fixes.

Changes

VybeClip Rebrand

Layer / File(s) Summary
Build and package identity rename
electron-builder.json5, package.json, electron/appPaths.ts, electron/windows.ts, electron/main.ts
appId, productName, publish owner, macOS usage descriptions, Windows executable name, dev user-data path, window icon, and AppUserModelId updated to VybeClip.
Documentation rebrand
README.md, README.zh-CN.md, CONTRIBUTING.md, _documentation/*
README/CONTRIBUTING rewritten for VybeClip and AGPLv3; new MVP release doc and Studio mockup HTML added.
Locale string rebrand
src/i18n/locales/*/*.json
App name, editor title, update messages, permission prompts, and settings labels changed from Recordly to VybeClip across all locales.
In-app UI text and dialog rebrand
src/App.tsx, electron/updater.ts, src/hooks/useScreenRecorder.ts, electron/ipc/register/recording.ts, src/lib/exporter/exportSavePolicy.ts, electron/ipc/register/sources.ts, src/components/video-editor/VideoEditor.tsx, electron/ipc/captions/generate.ts
Window titles, update dialogs, permission alerts, recording rejection text, and export error text updated to VybeClip.
Project file extension migration
electron/ipc/constants.ts, electron/ipc/project/manager.test.ts, electron/ipc/register/project.ts, src/components/video-editor/VideoEditor.tsx
Primary extension changed to .vybeclip with .recordly/.openscreen retained as legacy; save-name normalization and UI display updated.

Security Hardening

Layer / File(s) Summary
Whisper executable path allowlist
electron/ipc/state.ts, electron/ipc/utils.ts, electron/ipc/captions/generate.ts, electron/ipc/register/captions.ts
Adds approved-path allowlist and validates resolveWhisperExecutablePath against it.
Trusted project-open and asset read allowlisting
electron/ipc/project/manager.ts, electron/ipc/register/project.ts, electron/ipc/register/assets.ts
Adds isTrustedOpenableProjectPath and tightens local file reads with canonicalization re-checks.
Export write path allowlist
electron/ipc/register/export.ts
Adds isAllowedExportWritePath and enforces it before writing exported video files.
Generic error message hardening
electron/ipc/register/project.ts, electron/ipc/register/captions.ts, electron/ipc/register/settings.ts
Replaces error stringification with fixed generic error messages.
Marketplace install gating
electron/extensions/extensionMarketplace.ts, src/components/video-editor/ExtensionManager.tsx, src/i18n/locales/en/extensions.json
Blocks remote marketplace installs unless enabled and review-approved; UI shows "coming soon" states.
Native helper manifest verification and build/signing scripts
scripts/*.mjs, .github/workflows/*.yml
Hard-fails builds on recorded-entry verification failures, pins Whisper archive checksum, adds macOS signing script and CI signing-secret validation.
Per-extension namespaced settings storage
src/lib/extensions/extensionHost.ts
Migrates shared extension settings to per-extension namespaced storage keys.
Extension install path trust and CSP/webSecurity hardening
electron/extensions/extensionIpc.ts, electron/main.ts, electron/windows.ts
Restricts folder installs to dialog results and adds dynamic CSP header injection while removing webSecurity: false overrides.

VybeClip Studio Feature

Layer / File(s) Summary
Studio window creation and app routing
electron/windows.ts, electron/main.ts, src/App.tsx, electron/preload.ts, electron/electron-env.d.ts
Adds createStudioWindow, wires window/tray/quit flow, and adds showRecordingControls IPC.
VybeStudioPreview UI and studioActions helpers
src/components/vybe/*, src/components/brand/VybeClipLogo.tsx
New Studio home screen with recent projects, permissions banner, and record/import/open actions backed by tested action helpers.

Editor UI Theming and Export Fixes

Layer / File(s) Summary
Launch and app color palette rebrand
src/components/launch/*, src/index.css
Updates CSS variables/classes to a new orange brand accent palette.
TutorialHelp About/Feedback dialog rework
src/components/video-editor/TutorialHelp.tsx, src/components/video-editor/VideoEditor.tsx
Replaces Discord link with an AboutDialog and VybeClip issues link.
Export cleanup, GIF/native fallback, and caption/zoom fixes
src/components/video-editor/VideoEditor.tsx, src/lib/exporter/gifExporter.ts, src/lib/exporter/modernVideoExporter.ts, src/components/video-editor/VideoPlayback.tsx, src/components/video-editor/videoPlayback/zoomRegionUtils.ts
Adds warned temp-file discard, fixes GIF export rejection, RAF-coalesced caption clip-path updates, and zoom-transition boundary handling.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VybeStudioPreview
  participant studioActions
  participant ElectronMain
  participant HUDOverlay
  VybeStudioPreview->>studioActions: startStudioRecording(api)
  studioActions->>ElectronMain: check screen recording / accessibility permissions
  ElectronMain-->>studioActions: permission status
  studioActions->>ElectronMain: showRecordingControls()
  ElectronMain->>HUDOverlay: showOrCreateRecordingControls()
  studioActions->>ElectronMain: openSourceSelector()
Loading
sequenceDiagram
  participant Renderer
  participant ExportIPC
  participant PathValidator
  participant FileSystem
  Renderer->>ExportIPC: write-exported-video-to-path(outputPath)
  ExportIPC->>PathValidator: isAllowedExportWritePath(resolvedPath)
  PathValidator->>FileSystem: realpathSync(parentDir)
  FileSystem-->>PathValidator: canonical path
  PathValidator-->>ExportIPC: allowed / rejected
  ExportIPC-->>Renderer: success or failure response
Loading

Possibly related PRs

Suggested labels: Slop

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Alfredoalv13 Alfredoalv13 deleted the fix/mvp-pretest-review-findings branch July 2, 2026 01:15
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

⚠️ This pull request has been flagged by Anti-Slop.
Our automated checks detected patterns commonly associated with
low-quality or automated/AI submissions (failure count reached).
No automatic closure — a maintainer will review it.
If this is legitimate work, please add more context, link issues, or ping us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants