cornelsen-offline-lernen-bin

maintainer micleh · 0 votes · scanned 2026-08-03 00:08:14.047287
LOW
View on AUR ↗
Why flagged The package downloads a source archive from the official vendor domain (ebook.cornelsen.de) and applies benign patches to the app's JavaScript files to improve offline functionality; the use of npx is for legitimate build tools (@electron/asar) and does not execute untrusted remote code.

Triggered rules

LOW AI review downgraded a static finding llm_review

The static rules flagged this MEDIUM, but an AI model (qwen/qwen3-235b-a22b-2507) reviewed the full PKGBUILD and judged it LOW (confidence 95%): The package downloads a source archive from the official vendor domain (ebook.cornelsen.de) and applies benign patches to the app's JavaScript files to improve offline functionality; the use of npx is for legitimate build tools (@electron/asar) and does not execute untrusted remote code.

2 higher static findings superseded - not the current verdict (shown for transparency)
MEDIUM npx/bunx/deno executes a remote package remote_code_tool

`npx`/`bunx`/`pnpm dlx`/`deno run <url>` downloads AND runs a remote package at build time — the moral equivalent of piping a download into a shell. Severity downgraded: Node.js consumer context.

  • PKGBUILD:66 npx --yes @electron/asar extract "$_asar_path" "$_tmp_dir"
  • PKGBUILD:191 npx --yes @electron/asar pack "$_tmp_dir" "$_asar_path"
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:12 source=("${pkgname}-${pkgver}.zip::https://ebook.cornelsen.de/uma20/public/v2/uma/offline/win"

PKGBUILD

3 offending line(s) highlighted
1# Maintainer: MicLeh <micleh at proton dot me>
2pkgname=cornelsen-offline-lernen-bin
3pkgver=37.10.2
4# App version 2026.8.1 from 2026-05-07
5pkgrel=9
6pkgdesc="Cornelsen Offline Lernen Electron App"
7arch=('x86_64')
8url="https://www.cornelsen.de"
9license=('custom')
10depends=('electron')
11makedepends=('npm')
12source=("${pkgname}-${pkgver}.zip::https://ebook.cornelsen.de/uma20/public/v2/uma/offline/win"
13 "${pkgname}.sh"
14 "${pkgname}.desktop"
15 "icon16.png"
16 "icon32.png"
17 "icon96.png")
18b2sums=('04373e18c885017ec1233334264ca38ea1025446055d62eba3d207e0013b0bd13020e8e76be8791678d7ed9f1bc7037cdb8d4d771285c8ae4b0c9470cb67d210'
19 '2f1c802ebb340472a192316bdd9a7834dcb9e205e6a5fb186684c6142efc1033ff76c7238e9753b0bfa3f2daccebce6c709332b38b4a84440ffb42f316373531'
20 '8157b061d35da630252a8c402a98fb978fcad59fcfa813eb8b67dfeaae7e467051d659201e55b5d1811ae15eca49e47189a5a9840277f09876cf1d7021cfc1da'
21 '8dd68d9cd90347b954da9280d2133bc45706eec1e74261bdf19f53b61150ee5168db20a0f217643230c4dc66fb840f9ef61fce5f6aff5853b1bebae4bc9f4c65'
22 'b326c7f468c3cecb0e2618bae56092b7c6c5fa7e8f43f499645df01c2b468e74052bef51d48b36a8a7ade2fa12f9612591eaf38819998868271ec23abd366ad5'
23 '302bef8514b991cb6903d2def32abf348329a1cf104009b3c50c0e83e44b15103b89f669fb2012894df0c7d6ab926151f09e0cf61aff4102296be7071f66f794')
24
25_find_asar_path() {
26 local _path
27 local _candidates=(
28 "src/windows/resources/app.asar"
29 "src/src/windows/resources/app.asar"
30 "windows/resources/app.asar"
31 "resources/app.asar"
32 )
33
34 for _path in "${_candidates[@]}"; do
35 if [ -f "$_path" ]; then
36 printf '%s\n' "$_path"
37 return 0
38 fi
39 done
40
41 _path="$(find . -type f -path '*/resources/app.asar' | head -n 1)"
42 if [ -n "$_path" ]; then
43 printf '%s\n' "${_path#./}"
44 return 0
45 fi
46
47 return 1
48}
49
50prepare() {
51 local _asar_path
52 _asar_path="$(_find_asar_path || true)"
53
54 if [ -z "$_asar_path" ] || [ ! -f "$_asar_path" ]; then
55 echo "ERROR: source app.asar not found in extracted zip"
56 exit 1
57 fi
58
59 local _tmp_base _tmp_dir
60 _tmp_base="${TMPDIR:-/var/tmp}"
61 if [ ! -w "$_tmp_base" ]; then
62 _tmp_base="$PWD"
63 fi
64 _tmp_dir="$(mktemp -d "$_tmp_base/${pkgname}.asar.XXXXXX")"
65
66 npx --yes @electron/asar extract "$_asar_path" "$_tmp_dir"
67
68 node - "$_tmp_dir" <<'NODE'
69const fs = require('fs');
70const path = require('path');
71const root = process.argv[2];
72
73function collectJsFiles(dir, out = []) {
74 for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
75 const p = path.join(dir, ent.name);
76 if (ent.isDirectory()) {
77 collectJsFiles(p, out);
78 } else if (ent.isFile() && ent.name.endsWith('.js')) {
79 out.push(p);
80 }
81 }
82 return out;
83}
84
85const distDir = path.join(root, 'dist');
86if (!fs.existsSync(distDir)) {
87 console.error('ERROR: unpacked asar does not contain a dist directory');
88 process.exit(1);
89}
90
91const jsFiles = collectJsFiles(distDir);
92
93const verbose = process.env.CORNELSEN_PATCH_VERBOSE === '1';
94const strict = process.env.CORNELSEN_PATCH_STRICT === '1';
95
96const patches = [
97 {
98 name: 'annotations-404-fallback',
99 regex: /getProductAnnotations\(r,s=!1\)\{return this\.http\.get\(`\$\{s\?os\.getSyncApiBaseUrl\(\):os\.getApiBaseUrl\(\)\}\/pspdf\/annotations\/\$\{r\}`\)\.pipe\(/g,
100 replaceWith: 'getProductAnnotations(r,s=!1){return this.http.get(`${s?os.getSyncApiBaseUrl():os.getApiBaseUrl()}/pspdf/annotations/${r}`).pipe(ip(y=>s&&y.status===404?hs({}):r0(()=>y)),',
101 from: 'getProductAnnotations(r,s=!1){return this.http.get(`${s?os.getSyncApiBaseUrl():os.getApiBaseUrl()}/pspdf/annotations/${r}`).pipe(ip(y=>r0(()=>y)),Jl(y=>this.mapOfflinePdfIds(y)))}',
102 fromAlt: 'getProductAnnotations(r,s=!1){return this.http.get(`${s?ls.getSyncApiBaseUrl():ls.getApiBaseUrl()}/pspdf/annotations/${r}`).pipe(nc(y=>this.mapOfflinePdfIds(y)))}',
103 toAlt: 'getProductAnnotations(r,s=!1){return this.http.get(`${s?ls.getSyncApiBaseUrl():ls.getApiBaseUrl()}/pspdf/annotations/${r}`).pipe(op(y=>s&&y.status===404?fs({}):r0(()=>y)),nc(y=>this.mapOfflinePdfIds(y)))}',
104 fromAlt2: 'getProductAnnotations(r,s=!1){return this.http.get(`${s?cs.getSyncApiBaseUrl():cs.getApiBaseUrl()}/pspdf/annotations/${r}`).pipe(nc(y=>this.mapOfflinePdfIds(y)))}',
105 toAlt2: 'getProductAnnotations(r,s=!1){return this.http.get(`${s?cs.getSyncApiBaseUrl():cs.getApiBaseUrl()}/pspdf/annotations/${r}`).pipe(op(y=>s&&y.status===404?Es({}):r0(()=>y)),nc(y=>this.mapOfflinePdfIds(y)))}',
106 fromAlt3: 'getProductAnnotations(i,s=!1){return this.http.get(`${s?cs.getSyncApiBaseUrl():cs.getApiBaseUrl()}/pspdf/annotations/${i}`).pipe(nc(y=>this.mapOfflinePdfIds(y)))}',
107 toAlt3: 'getProductAnnotations(i,s=!1){return this.http.get(`${s?cs.getSyncApiBaseUrl():cs.getApiBaseUrl()}/pspdf/annotations/${i}`).pipe(ip(y=>{if(s&&y.status===404)return _s({});throw y}),nc(y=>this.mapOfflinePdfIds(y)))}',
108 to: 'getProductAnnotations(r,s=!1){return this.http.get(`${s?os.getSyncApiBaseUrl():os.getApiBaseUrl()}/pspdf/annotations/${r}`).pipe(ip(y=>s&&y.status===404?hs({}):r0(()=>y)),Jl(y=>this.mapOfflinePdfIds(y)))}',
109 alreadyMarker: 's&&y.status===404?hs({}):r0(()=>y)',
110 alreadyMarker2: 's&&y.status===404?Es({}):r0(()=>y)',
111 alreadyMarker3: 'if(s&&y.status===404)return _s({})',
112 required: false
113 },
114 {
115 name: 'compatibility-401-fallback',
116 from: 'isCompatibleWithOnline$(){return this.http.get(`${os.getSyncApiBaseUrl()}/compatibility/offlineClients/${GE}`).pipe(sa(r=>r.isCompatible))}',
117 fromAlt: 'isCompatibleWithOnline$(){return this.http.get(`${ls.getSyncApiBaseUrl()}/compatibility/offlineClients/${qE}`).pipe(sa(r=>r.isCompatible))}',
118 toAlt: 'isCompatibleWithOnline$(){return this.http.get(`${ls.getSyncApiBaseUrl()}/compatibility/offlineClients/${qE}`).pipe(sa(r=>r.isCompatible),op(r=>r.status===401?fs(!0):r0(()=>r)))}',
119 fromAlt2: 'isCompatibleWithOnline$(){return this.http.get(`${cs.getSyncApiBaseUrl()}/compatibility/offlineClients/${ZE}`).pipe(sa(r=>r.isCompatible))}',
120 toAlt2: 'isCompatibleWithOnline$(){return this.http.get(`${cs.getSyncApiBaseUrl()}/compatibility/offlineClients/${ZE}`).pipe(sa(r=>r.isCompatible),op(r=>r.status===401?Es(!0):r0(()=>r)))}',
121 fromAlt3: 'isCompatibleWithOnline$(){return this.http.get(`${cs.getSyncApiBaseUrl()}/compatibility/offlineClients/${QE}`).pipe(ta(i=>i.isCompatible))}',
122 toAlt3: 'isCompatibleWithOnline$(){return this.http.get(`${cs.getSyncApiBaseUrl()}/compatibility/offlineClients/${QE}`).pipe(ta(i=>i.isCompatible),ip(r=>{if(r.status===401)return _s(!0);throw r}))}',
123 to: 'isCompatibleWithOnline$(){return this.http.get(`${os.getSyncApiBaseUrl()}/compatibility/offlineClients/${GE}`).pipe(sa(r=>r.isCompatible),ip(r=>r.status===401?hs(!0):r0(()=>r)))}',
124 alreadyMarker: 'r.status===401?hs(!0):r0(()=>r)',
125 alreadyMarker2: 'r.status===401?Es(!0):r0(()=>r)',
126 alreadyMarker3: 'r.status===401)return _s(!0)',
127 required: false
128 }
129];
130
131for (const p of patches) {
132 let patched = false;
133
134 for (const filePath of jsFiles) {
135 let txt = fs.readFileSync(filePath, 'utf8');
136
137 if (txt.includes(p.to) || (p.toAlt && txt.includes(p.toAlt)) || (p.toAlt2 && txt.includes(p.toAlt2)) || (p.toAlt3 && txt.includes(p.toAlt3)) || txt.includes(p.alreadyMarker) || (p.alreadyMarker2 && txt.includes(p.alreadyMarker2)) || (p.alreadyMarker3 && txt.includes(p.alreadyMarker3))) {
138 patched = true;
139 break;
140 }
141
142 if (p.regex && p.regex.test(txt)) {
143 txt = txt.replace(p.regex, p.replaceWith);
144 fs.writeFileSync(filePath, txt);
145 patched = true;
146 break;
147 }
148
149 if (txt.includes(p.from)) {
150 txt = txt.replace(p.from, p.to);
151 fs.writeFileSync(filePath, txt);
152 patched = true;
153 break;
154 }
155
156 if (p.fromAlt && p.toAlt && txt.includes(p.fromAlt)) {
157 txt = txt.replace(p.fromAlt, p.toAlt);
158 fs.writeFileSync(filePath, txt);
159 patched = true;
160 break;
161 }
162
163 if (p.fromAlt2 && p.toAlt2 && txt.includes(p.fromAlt2)) {
164 txt = txt.replace(p.fromAlt2, p.toAlt2);
165 fs.writeFileSync(filePath, txt);
166 patched = true;
167 break;
168 }
169
170 if (p.fromAlt3 && p.toAlt3 && txt.includes(p.fromAlt3)) {
171 txt = txt.replace(p.fromAlt3, p.toAlt3);
172 fs.writeFileSync(filePath, txt);
173 patched = true;
174 break;
175 }
176 }
177
178 if (!patched) {
179 if (p.required || strict) {
180 console.error(`ERROR: required patch target not found: ${p.name}`);
181 process.exit(1);
182 }
183
184 if (verbose) {
185 console.warn(`INFO: optional patch target not found: ${p.name} (continuing)`);
186 }
187 }
188}
189NODE
190
191 npx --yes @electron/asar pack "$_tmp_dir" "$_asar_path"
192 rm -rf "$_tmp_dir"
193}
194
195package() {
196 local _asar_path
197 _asar_path="$(_find_asar_path || true)"
198
199 if [ ! -f "$_asar_path" ]; then
200 echo "ERROR: app.asar not found"
201 exit 1
202 fi
203
204 install -Dm644 "$_asar_path" "$pkgdir/usr/share/$pkgname/app.asar"
205 install -Dm755 "${pkgname}.sh" "$pkgdir/usr/bin/$pkgname"
206 install -Dm644 "${pkgname}.desktop" "$pkgdir/usr/share/applications/${pkgname}.desktop"
207
208 # Icons
209 install -Dm644 "icon16.png" "$pkgdir/usr/share/icons/hicolor/16x16/apps/${pkgname}.png"
210 install -Dm644 "icon32.png" "$pkgdir/usr/share/icons/hicolor/32x32/apps/${pkgname}.png"
211 install -Dm644 "icon96.png" "$pkgdir/usr/share/icons/hicolor/96x96/apps/${pkgname}.png"
212
213 # Licenses
214 _license=$(find . -name "LICENSE" | head -n 1)
215 if [ -n "$_license" ]; then
216 install -Dm644 "$_license" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
217 fi
218}
219

Changes since previous scan

--- PKGBUILD @ 2026-07-19 00:17
+++ PKGBUILD @ 2026-08-03 00:08
@@ -2,7 +2,7 @@
pkgname=cornelsen-offline-lernen-bin
pkgver=37.10.2
# App version 2026.8.1 from 2026-05-07
-pkgrel=8
+pkgrel=9
pkgdesc="Cornelsen Offline Lernen Electron App"
arch=('x86_64')
url="https://www.cornelsen.de"
@@ -15,7 +15,7 @@
"icon16.png"
"icon32.png"
"icon96.png")
-b2sums=('4888706d794bdbc5fd5544a920d02254f7d14e9a49f25e946cfae261aa66f171a9b0048e5bd39e72797ca73c64f83aee73d1186604fbd9ad97a65a545bfc7036'
+b2sums=('04373e18c885017ec1233334264ca38ea1025446055d62eba3d207e0013b0bd13020e8e76be8791678d7ed9f1bc7037cdb8d4d771285c8ae4b0c9470cb67d210'
'2f1c802ebb340472a192316bdd9a7834dcb9e205e6a5fb186684c6142efc1033ff76c7238e9753b0bfa3f2daccebce6c709332b38b4a84440ffb42f316373531'
'8157b061d35da630252a8c402a98fb978fcad59fcfa813eb8b67dfeaae7e467051d659201e55b5d1811ae15eca49e47189a5a9840277f09876cf1d7021cfc1da'
'8dd68d9cd90347b954da9280d2133bc45706eec1e74261bdf19f53b61150ee5168db20a0f217643230c4dc66fb840f9ef61fce5f6aff5853b1bebae4bc9f4c65'

Scan history

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

Report a package

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

0 / 4000
Your suggestion