claude-cowork-linux

maintainer johnzfitch · 3 votes · scanned 2026-08-03 00:08:14.047287
MEDIUM
View on AUR ↗
Why flagged This PKGBUILD executes code from a personal GitHub repository (johnzfitch/claude-cowork-linux) with 'SKIP' checksum verification, then uses that repo's own fetch-dmg.js script to dynamically determine and download a DMG URL at build time with no integrity check. The DMG is Anthropic's proprietary macOS Claude Desktop binary, which is then unpacked, patched by enable-cowork.py (also from the same unverified repo), and repacked. The core concerns are: (1) the git source has SKIP checksum so any commit to that repo could inject malicious code into fetch-dmg.js, enable-cowork.py, or the stub JS files that get baked into the final asar; (2) the DMG download URL is dynamically resolved at build time with no hash verification, allowing CDN-level substitution; (3) npm installs @electron/asar from the npm registry without a lockfile or integrity pin. The stubs/cowork/*.sh files are installed as executable scripts. This is a genuine medium-severity supply-chain concern: unverified executed code from a personal host modifying and repackaging proprietary binaries, not a false positive. It is not high because there is no evidence of active malice or obfuscation — it appears to be a legitimate but poorly secured community packaging effort.

Triggered rules

MEDIUM npm/yarn/pnpm install of an undeclared external package npm_install_external

Runs `npm/yarn/pnpm install <package>` for a package not in source=(), pulling unpinned, unreviewed code at build time. Severity downgraded: the package declares/looks like a Node.js consumer, where build-time installs are expected.

  • PKGBUILD:66 npm install --prefix "${srcdir}" @electron/asar >/dev/null 2>&1
MEDIUM AI review llm_review

An AI model (anthropic/claude-4.6-sonnet-20260217) reviewed this and agrees it is MEDIUM (confidence 82%): This PKGBUILD executes code from a personal GitHub repository (johnzfitch/claude-cowork-linux) with 'SKIP' checksum verification, then uses that repo's own fetch-dmg.js script to dynamically determine and download a DMG URL at build time with no integrity check. The DMG is Anthropic's proprietary macOS Claude Desktop binary, which is then unpacked, patched by enable-cowork.py (also from the same unverified repo), and repacked. The core concerns are: (1) the git source has SKIP checksum so any commit to that repo could inject malicious code into fetch-dmg.js, enable-cowork.py, or the stub JS files that get baked into the final asar; (2) the DMG download URL is dynamically resolved at build time with no hash verification, allowing CDN-level substitution; (3) npm installs @electron/asar from the npm registry without a lockfile or integrity pin. The stubs/cowork/*.sh files are installed as executable scripts. This is a genuine medium-severity supply-chain concern: unverified executed code from a personal host modifying and repackaging proprietary binaries, not a false positive. It is not high because there is no evidence of active malice or obfuscation — it appears to be a legitimate but poorly secured community packaging effort.

PKGBUILD

1 offending line(s) highlighted
1# Maintainer: Zack Fitch <zack@internetuniverse.org>
2pkgname=claude-cowork-linux
3pkgver=1.1.4010
4pkgrel=10
5pkgdesc="Anthropic Claude Desktop with Cowork (local agent) support for Linux"
6arch=('x86_64')
7url="https://github.com/johnzfitch/claude-cowork-linux"
8license=('custom:proprietary')
9depends=(
10 'electron'
11 'nodejs'
12)
13# gnome-keyring is recommended but not required; launcher detects SecretService
14# at runtime and falls back to --password-store=basic if unavailable
15makedepends=(
16 'p7zip'
17 'npm'
18 'curl'
19)
20optdepends=(
21 'xdg-utils: for opening URLs'
22 'bubblewrap: for sandbox isolation'
23 'gnome-keyring: SecretService provider for secure credential storage'
24 'kwallet: SecretService provider for KDE users'
25 'python: for enable-cowork.py patch script'
26)
27provides=('claude-cowork' 'claude-desktop')
28conflicts=(
29 'claude-cowork'
30 'claude-desktop'
31 'claude-desktop-bin'
32 'claude-desktop-native'
33 'claude-desktop-appimage'
34)
35options=('!strip')
36
37source=(
38 "git+https://github.com/johnzfitch/claude-cowork-linux.git"
39)
40sha256sums=(
41 'SKIP'
42)
43
44pkgver() {
45 cd "${srcdir}"
46
47 # Use Node.js to query the DMG API
48 local version
49 version=$(node "${srcdir}/claude-cowork-linux/fetch-dmg.js" 2>/dev/null \
50 | awk '{print $1}')
51
52 echo "${version:-1.1.4010}"
53}
54
55prepare() {
56 cd "${srcdir}"
57
58 # Fetch latest DMG URL via Node.js, download with curl
59 echo "Fetching latest Claude Desktop DMG URL..."
60 local dmg_url
61 dmg_url=$(node "${srcdir}/claude-cowork-linux/fetch-dmg.js" --url)
62 echo "Downloading DMG from CDN..."
63 curl -fSL --progress-bar -o "${srcdir}/Claude.dmg" "$dmg_url"
64
65 # Install asar tool locally
66 npm install --prefix "${srcdir}" @electron/asar >/dev/null 2>&1
67}
68
69build() {
70 cd "${srcdir}"
71
72 local _asar="${srcdir}/node_modules/.bin/asar"
73 local _repo="${srcdir}/claude-cowork-linux"
74
75 # Extract DMG with 7z
76 echo "Extracting DMG..."
77 local seven_z_exit=0
78 7z x -y "${srcdir}/Claude.dmg" -o"${srcdir}/dmg-extracted" >/dev/null 2>&1 || seven_z_exit=$?
79 # 7z exit 1 = warning (e.g. "Dangerous link path" for /Applications symlink)
80 if [[ $seven_z_exit -gt 2 ]]; then
81 echo "Error: Failed to extract DMG (7z exit code: $seven_z_exit)"
82 return 1
83 fi
84
85 # Find Claude.app and app.asar
86 local _claude_app
87 _claude_app=$(find "${srcdir}/dmg-extracted" -name "Claude.app" -type d | head -1)
88 if [[ -z "$_claude_app" ]]; then
89 echo "Error: Claude.app not found in DMG"
90 return 1
91 fi
92
93 local _app_asar="${_claude_app}/Contents/Resources/app.asar"
94 if [[ ! -f "$_app_asar" ]]; then
95 echo "Error: app.asar not found at: $_app_asar"
96 return 1
97 fi
98
99 # Extract app.asar
100 "$_asar" extract "$_app_asar" "${srcdir}/linux-app-extracted"
101
102 # Copy unpacked native modules if present
103 local _unpacked="${_claude_app}/Contents/Resources/app.asar.unpacked"
104 if [[ -d "$_unpacked" ]]; then
105 cp -r "$_unpacked"/* "${srcdir}/linux-app-extracted/" 2>/dev/null || true
106 fi
107
108 # Copy resources/ from DMG (i18n, icons, etc.) excluding the asar itself
109 local _resources_dir="${_claude_app}/Contents/Resources"
110 mkdir -p "${srcdir}/linux-app-extracted/resources"
111 for item in "$_resources_dir"/*; do
112 local name
113 name=$(basename "$item")
114 case "$name" in
115 app.asar|app.asar.unpacked) continue ;;
116 esac
117 cp -r "$item" "${srcdir}/linux-app-extracted/resources/$name" 2>/dev/null || true
118 done
119
120 # Bake stubs into node_modules
121 mkdir -p "${srcdir}/linux-app-extracted/node_modules/@ant/claude-swift/js"
122 mkdir -p "${srcdir}/linux-app-extracted/node_modules/@ant/claude-native"
123 cp -f "${_repo}/stubs/@ant/claude-swift/js/index.js" \
124 "${srcdir}/linux-app-extracted/node_modules/@ant/claude-swift/js/index.js"
125 cp -f "${_repo}/stubs/@ant/claude-native/index.js" \
126 "${srcdir}/linux-app-extracted/node_modules/@ant/claude-native/index.js"
127
128 # Copy frame-fix files
129 cp -f "${_repo}/stubs/frame-fix/frame-fix-entry.js" \
130 "${srcdir}/linux-app-extracted/frame-fix-entry.js"
131 cp -f "${_repo}/stubs/frame-fix/frame-fix-wrapper.js" \
132 "${srcdir}/linux-app-extracted/frame-fix-wrapper.js"
133
134 # Copy cowork orchestration modules
135 mkdir -p "${srcdir}/linux-app-extracted/cowork"
136 cp -f "${_repo}"/stubs/cowork/*.js \
137 "${srcdir}/linux-app-extracted/cowork/"
138 cp -f "${_repo}"/stubs/cowork/*.sh \
139 "${srcdir}/linux-app-extracted/cowork/" 2>/dev/null || true
140
141 # Apply cowork patch
142 echo "Applying cowork patch..."
143 python "${_repo}/enable-cowork.py" \
144 "${srcdir}/linux-app-extracted/.vite/build/index.js"
145
146 # Repack into app.asar
147 echo "Repacking app.asar..."
148 "$_asar" pack "${srcdir}/linux-app-extracted" "${srcdir}/app.asar"
149}
150
151package() {
152 cd "${srcdir}"
153
154 # Install repacked app.asar
155 install -Dm644 "${srcdir}/app.asar" \
156 "${pkgdir}/usr/lib/claude-cowork/app.asar"
157
158 # Install i18n locale JSON files to electron's resources directory.
159 # The app reads these via process.resourcesPath at startup, which resolves
160 # to the system electron's resources dir -- not inside the asar.
161 local _electron_name
162 _electron_name=$(basename "$(readlink -f /usr/bin/electron)")
163 local _electron_resources="/usr/lib/${_electron_name}/resources"
164 mkdir -p "${pkgdir}${_electron_resources}"
165 install -m644 "${srcdir}/linux-app-extracted/resources/"*.json \
166 "${pkgdir}${_electron_resources}/"
167
168 # Install plugin permission shim to electron resources dir.
169 # The asar copies this to <sessionStorageDir>/shim-lib/shim.sh at session start.
170 install -m755 "${srcdir}/linux-app-extracted/cowork/cowork-plugin-shim.sh" \
171 "${pkgdir}${_electron_resources}/cowork-plugin-shim.sh"
172
173 # Install launcher script
174 install -Dm755 /dev/stdin "${pkgdir}/usr/bin/claude-cowork" <<'EOF'
175#!/bin/bash
176# Claude Cowork Linux launcher
177
178if [[ -n "$WAYLAND_DISPLAY" ]] || [[ "$XDG_SESSION_TYPE" == "wayland" ]]; then
179 export ELECTRON_OZONE_PLATFORM_HINT=wayland
180fi
181
182# Detect password store backend
183PW_STORE="gnome-libsecret"
184if ! dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus \
185 org.freedesktop.DBus.NameHasOwner string:"org.freedesktop.secrets" 2>/dev/null \
186 | grep -q "boolean true"; then
187 PW_STORE="basic"
188fi
189
190exec electron /usr/lib/claude-cowork/app.asar \
191 --no-sandbox \
192 --disable-gpu \
193 --password-store="$PW_STORE" \
194 --enable-features=GlobalShortcutsPortal "$@"
195EOF
196
197 # Install desktop entry
198 install -Dm644 /dev/stdin "${pkgdir}/usr/share/applications/claude-cowork.desktop" <<EOF
199[Desktop Entry]
200Name=Claude Cowork
201Comment=Anthropic Claude Desktop with local agent support
202Exec=claude-cowork %U
203Icon=claude-cowork
204Type=Application
205Categories=Development;Utility;
206MimeType=x-scheme-handler/claude;
207StartupWMClass=Claude
208EOF
209
210 # Extract icon from DMG's Claude.app if available
211 local _claude_app
212 _claude_app=$(find "${srcdir}/dmg-extracted" -name "Claude.app" -type d 2>/dev/null | head -1)
213 if [[ -n "$_claude_app" ]]; then
214 local _icns="${_claude_app}/Contents/Resources/AppIcon.icns"
215 # Try to convert .icns to png (icns2png from libicns)
216 if [[ -f "$_icns" ]] && command -v icns2png &>/dev/null; then
217 icns2png -x -s 256 "$_icns" -o "${srcdir}/" 2>/dev/null || true
218 local _icon
219 _icon=$(ls -S "${srcdir}/"*.png 2>/dev/null | head -1)
220 if [[ -n "$_icon" ]]; then
221 install -Dm644 "$_icon" \
222 "${pkgdir}/usr/share/icons/hicolor/256x256/apps/claude-cowork.png"
223 fi
224 fi
225 fi
226
227 # Install license notice
228 install -Dm644 /dev/stdin "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" <<EOF
229Claude Desktop is proprietary software by Anthropic PBC.
230This package provides a Linux compatibility layer for the macOS app.
231See https://www.anthropic.com/legal/consumer-terms for terms of service.
232EOF
233}
234

Scan history

Scanned at (UTC)SeverityRules
2026-08-03 00:08:14 MEDIUM 2
2026-08-02 00:16:08 MEDIUM 2
2026-08-01 00:11:18 MEDIUM 2
2026-07-31 00:14:10 MEDIUM 2
2026-07-30 00:17:23 MEDIUM 2
2026-07-29 00:25:53 MEDIUM 2
2026-07-28 00:07:28 MEDIUM 2
2026-07-27 00:24:32 MEDIUM 2
2026-07-26 00:07:32 MEDIUM 2
2026-07-25 00:13:44 MEDIUM 2
2026-07-24 00:02:28 MEDIUM 2
2026-07-23 00:14:47 MEDIUM 2
2026-07-22 00:29:32 MEDIUM 2
2026-07-21 00:24:15 MEDIUM 2
2026-07-20 00:19:49 MEDIUM 2
2026-07-19 00:17:08 MEDIUM 2
2026-07-18 00:14:48 MEDIUM 2
2026-07-17 00:06:16 MEDIUM 2
2026-07-16 00:05:41 MEDIUM 2
2026-07-15 00:09:25 MEDIUM 2

Report a package

Reports go to the AURWatch maintainer (one person) and are read by hand. No login required.

0 / 4000
Your suggestion