opencode-desktop-bin

MEDIUM
maintainer lapsus 20 votes scanned 2026-11-04 00:25:23.536954
View on AUR
Why flagged

The package downloads prebuilt .deb binaries from a non-whitelisted host (opencode.ai), which are then extracted and repackaged; while the checksums are verified, the source is a third-party binary distribution not built from publicly auditable source, creating a supply-chain risk if the host is compromised.

Triggered rules

Medium External download from an untrusted host, not in source=() external_download_not_in_source

curl/wget fetches a URL on a non-allowlisted host that is not part of source=(), so it is not checksum-verified by makepkg.

  • PKGBUILD:29 curl -fsSL https://opencode.ai/update/api/latest/desktop/opencode | jq -r '.version'
Medium source=() URL on a non-standard host source_untrusted_domain

One or more source=() URLs point to a host outside the trusted allowlist (github.com, gitlab.com, codeberg.org, pypi.org, …).

  • PKGBUILD:33 source_x86_64=("opencode-desktop-${pkgver}-linux-amd64.deb::https://opencode.ai/files/bin/${pkgver}/opencode-desktop-linux-amd64.deb")
Medium AI review llm_review

An AI model (qwen/qwen3-235b-a22b-2507) reviewed this and agrees it is MEDIUM (confidence 90%): The package downloads prebuilt .deb binaries from a non-whitelisted host (opencode.ai), which are then extracted and repackaged; while the checksums are verified, the source is a third-party binary distribution not built from publicly auditable source, creating a supply-chain risk if the host is compromised.

PKGBUILD

2 offending line(s) highlighted
1# Maintainer: Yakov Till <yakov.till@gmail.com>
2
3pkgname=opencode-desktop-bin
4pkgver=2.2.5
5pkgrel=1
6pkgdesc="OpenCode desktop client"
7arch=('x86_64' 'aarch64')
8url="https://opencode.ai"
9license=('MIT')
10provides=('opencode-desktop')
11conflicts=('opencode-desktop')
12# Arch Linux ARM ships no electron package at all (which is also why official
13# code is absent there), so only x86_64 can run on a system runtime.
14_electron=electron44
15# Upstream names the bundled Electron binary after the app id and has already
16# renamed it once (ai.opencode.desktop -> ai.opencode), so keep the name in a
17# single place; the desktop Exec rewrite matches by path, not by name.
18_app_bin=ai.opencode
19depends=('ripgrep')
20depends_x86_64=("${_electron}")
21depends_aarch64=('gtk3' 'nss' 'libxss' 'libxtst' 'alsa-lib' 'libsecret' 'libnotify' 'xdg-utils')
22makedepends_x86_64=('asar')
23optdepends=('libappindicator-gtk3: tray icon support')
24# opencode-cli is a Bun standalone executable: stripping discards the embedded
25# bundle and leaves a bare Bun runtime that no longer knows the "serve" command.
26options=('!strip' '!debug')
27
28latestver() {
29 curl -fsSL https://opencode.ai/update/api/latest/desktop/opencode | jq -r '.version'
30}
31
32source=("LICENSE::https://raw.githubusercontent.com/anomalyco/opencode/v${pkgver}/LICENSE")
33source_x86_64=("opencode-desktop-${pkgver}-linux-amd64.deb::https://opencode.ai/files/bin/${pkgver}/opencode-desktop-linux-amd64.deb")
34source_aarch64=("opencode-desktop-${pkgver}-linux-arm64.deb::https://opencode.ai/files/bin/${pkgver}/opencode-desktop-linux-arm64.deb")
35sha256sums=('625f0f619133f89bbbb2abe37369613dfa1885eba1e50d02170deb62bb42cb6b')
36sha256sums_x86_64=('c827e714220de744c829fdea55540ed814b1372061e7a905fde0978f9f819de8')
37sha256sums_aarch64=('7517f7ef9c0a9e88cf0060911de21f1c3dfbfd796f535ca62fec6c968d0ec7e3')
38
39package() {
40 local _debarch=amd64
41 [[ "${CARCH}" == aarch64 ]] && _debarch=arm64
42 bsdtar -xf "${srcdir}/opencode-desktop-${pkgver}-linux-${_debarch}.deb" data.tar.xz
43 bsdtar -xf data.tar.xz -C "${pkgdir}"
44
45 local _appdir _exec
46 if [[ "${CARCH}" == x86_64 ]]; then
47 # The app only has to work on the Electron it was built against, so refuse
48 # to package a runtime pairing upstream never shipped.
49 local _bundled
50 _bundled=$(grep -aoP 'Electron/\K[0-9]+' "${pkgdir}/opt/OpenCode/${_app_bin}" | head -1)
51 if [[ "electron${_bundled}" != "${_electron}" ]]; then
52 echo "Upstream now bundles Electron ${_bundled:-<undetected>}; set _electron=electron${_bundled}" >&2
53 exit 1
54 fi
55
56 # Keep the app payload only; the bundled Chromium/Node runtime is replaced
57 # by the system electron, which ships its own sandbox, codecs and ICU data.
58 _appdir="${pkgdir}/usr/lib/opencode-desktop"
59 install -d "${_appdir}"
60 mv "${pkgdir}/opt/OpenCode/resources/"* "${_appdir}/"
61 rm -rf "${pkgdir}/opt"
62
63 # The app resolves opencode-cli relative to process.resourcesPath, which the
64 # system electron reports as its own directory. A shim app repoints it here
65 # and then loads the untouched asar.
66 asar extract-file "${_appdir}/app.asar" package.json
67 sed -i 's|"main": "./out/main/index.js"|"main": "./main.mjs"|' package.json
68 grep -q '"main": "./main.mjs"' package.json
69 install -Dm644 package.json "${_appdir}/package.json"
70 # The app stages its own copy of the bundled CLI under <userData>/cli/<version>
71 # and never prunes it, but skips the copy when that path already exists, so the
72 # shim pre-seeds a symlink and pacman keeps owning the only copy of the binary.
73 [[ -s "${_appdir}/opencode-cli.version" ]] || {
74 echo "upstream dropped opencode-cli.version; the CLI symlink seed has no version source" >&2
75 exit 1
76 }
77 install -Dm644 /dev/stdin "${_appdir}/main.mjs" <<'EOF'
78import { app } from "electron"
79import { existsSync, mkdirSync, readFileSync, symlinkSync } from "node:fs"
80import { dirname, join } from "node:path"
81
82Object.defineProperty(process, "resourcesPath", { value: import.meta.dirname, configurable: true })
83
84const linkCli = (userData) => {
85 try {
86 const version = readFileSync(join(import.meta.dirname, "opencode-cli.version"), "utf8").trim()
87 if (!version) return
88 const link = join(userData, "cli", version.replace(/[^a-zA-Z0-9._-]/g, "-"), "opencode-cli")
89 if (existsSync(link)) return
90 mkdirSync(dirname(link), { recursive: true })
91 symlinkSync(join(import.meta.dirname, "opencode-cli"), link)
92 } catch {}
93}
94
95const setPath = app.setPath.bind(app)
96app.setPath = (name, value) => {
97 setPath(name, value)
98 if (name === "userData") linkCli(value)
99}
100app.whenReady().then(() => linkCli(app.getPath("userData")))
101
102await import("./app.asar/out/main/index.js")
103EOF
104 _exec="${_electron} /usr/lib/opencode-desktop"
105 else
106 _appdir="${pkgdir}/opt/OpenCode/resources"
107 _exec="/opt/OpenCode/${_app_bin}"
108 fi
109
110 # Prune Debian/Ubuntu-specific and dead files
111 rm -rf "${pkgdir}/usr/share/doc"
112 rm -f "${_appdir}/apparmor-profile"
113 rm -f "${_appdir}/app-update.yml"
114
115 # Prune musl native modules (useless on glibc Arch)
116 find "${_appdir}" -name '*.musl.node' -delete
117 find "${_appdir}" -depth -type d -name '*-musl' -exec rm -rf {} +
118
119 # Launcher script (supports user flags and Wayland)
120 install -Dm755 /dev/stdin "${pkgdir}/usr/bin/opencode-desktop" <<'EOF'
121#!/bin/bash
122XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
123if [[ -f "$XDG_CONFIG_HOME/opencode-desktop-flags.conf" ]]; then
124 OPENCODE_USER_FLAGS="$(grep -v '^#' "$XDG_CONFIG_HOME/opencode-desktop-flags.conf")"
125fi
126exec @EXEC@ $OPENCODE_USER_FLAGS "$@"
127EOF
128 sed -i "s|@EXEC@|${_exec}|" "${pkgdir}/usr/bin/opencode-desktop"
129
130 # Point desktop files at the wrapper (icons/StartupWMClass already match upstream names)
131 sed -i 's|Exec=/opt/OpenCode/[^[:space:]]*|Exec=opencode-desktop|' \
132 "${pkgdir}"/usr/share/applications/*.desktop
133
134 install -Dm644 "${srcdir}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
135}
136

Changes since previous scan

--- PKGBUILD @ 2026-10-31 00:07
+++ PKGBUILD @ 2026-11-04 00:25
@@ -1,7 +1,7 @@
# Maintainer: Yakov Till <yakov.till@gmail.com>
pkgname=opencode-desktop-bin
-pkgver=2.2.3
+pkgver=2.2.5
pkgrel=1
pkgdesc="OpenCode desktop client"
arch=('x86_64' 'aarch64')
@@ -33,8 +33,8 @@
source_x86_64=("opencode-desktop-${pkgver}-linux-amd64.deb::https://opencode.ai/files/bin/${pkgver}/opencode-desktop-linux-amd64.deb")
source_aarch64=("opencode-desktop-${pkgver}-linux-arm64.deb::https://opencode.ai/files/bin/${pkgver}/opencode-desktop-linux-arm64.deb")
sha256sums=('625f0f619133f89bbbb2abe37369613dfa1885eba1e50d02170deb62bb42cb6b')
-sha256sums_x86_64=('98b6cccb1649e3c439bb1a5cf654c8310c6cf8b294a2444c540fd24cb781e99e')
-sha256sums_aarch64=('592153e7ad851dc5429e4961c25cf6562d2be1e4764dbdcbcb6f71807bd9f45b')
+sha256sums_x86_64=('c827e714220de744c829fdea55540ed814b1372061e7a905fde0978f9f819de8')
+sha256sums_aarch64=('7517f7ef9c0a9e88cf0060911de21f1c3dfbfd796f535ca62fec6c968d0ec7e3')
package() {
local _debarch=amd64

Scan history

Scanned at (UTC)SeverityRules
2026-11-04 00:25:23 Medium 3
2026-11-03 00:01:00 Medium 3
2026-11-02 00:26:00 Medium 3
2026-11-01 00:21:48 Medium 3
2026-10-31 01:57:41 Medium 3
2026-10-31 00:07:41 Medium 3
2026-10-30 15:56:05 Medium 3
2026-10-30 00:29:30 Medium 3
2026-10-29 15:52:51 Medium 3
2026-10-29 00:13:55 Medium 3
2026-10-28 00:06:54 Medium 3
2026-10-27 17:48:24 Medium 3
2026-10-27 00:23:28 Medium 3
2026-10-26 00:12:40 Medium 3
2026-10-25 00:20:48 Medium 3
2026-10-24 01:38:37 Medium 3
2026-10-24 00:14:55 Medium 3
2026-10-23 00:02:19 Medium 3
2026-10-22 17:35:31 Medium 3
2026-10-22 00:13:10 Medium 3

Report a package

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

0 / 4000
Your suggestion