// Git Pusher License Module v2.1 // (c) 2026 - Proprietary Software function _d(a,k){return a.map(function(c){return String.fromCharCode(c^k)}).join('')} var _yBOwzrHa='aC2WIHCyrXL55sdaZYmCeiGoDUD9uxbA'; var _iywTgOKM='c5cHGMalLoMFhLSNJzoSUFIKqfvwwSDA'; var _FqwiCUSy='D4lcGNtxgGEzBo0CmkIr0of2wuXCQvw1'; var _GzuzHWdA='UtReFKpwkgsosYxpdVpaVy7rxx4g0x5X'; var _JDwUeTZv='Z74KgA9iCjfNpZcExT8bY0mNeVOCAvHl'; var _bykrOplk='plqB89Gu7XPaC9jLlCgQi2yJadINDbeA'; var _TmuBSDob='w2D8yUMpFY6U5jzQLU46kNLNu2DNTmFJ'; var _oRPXAMTn='YeI5aaXSJ8f66xg0pq31m4xRTzO1NllD'; var _KNXWivCf='Y3T35X8uSJdTEn0bhpkKdi8jmWFrRF4g'; var _wRwdtsAl='wnFyASFDoyBYmaZw4MKRKnguQ20dsuYa'; function _xbRdZKKL(a,b){ var c=a^b;var d=c.toString(16); return d.split('').reverse().join(''); } function _hBTVdjAC(a,b){ var c=a^b;var d=c.toString(16); return d.split('').reverse().join(''); } function _FMolrKWt(a,b){ var c=a^b;var d=c.toString(16); return d.split('').reverse().join(''); } function _kyanRbHm(a,b){ var c=a^b;var d=c.toString(16); return d.split('').reverse().join(''); } function _aLzzRfEs(a,b){ var c=a^b;var d=c.toString(16); return d.split('').reverse().join(''); } var _htINRRRo=_d([149,149,149,149,149,250,253,255,241,246,152,232,237,250,244,241,251,152,243,253,225,149,149,149,149,149,178],184); var _ntgdrTCI=_d([65,69,69,79,69,102,77,66,78,107,103,125,100,103,101,75,53,123,60,78,77,93,73,74,77,77,67,79,77,107,52,77,65,69,69,79,79,107,71,79,77,107,73,77,98,102,62,100,67,107,58,61,93,53,103,53,101,118,56,89,57],12); var _jnIqxkUd=_d([203,186,196,223,233,236,199,255,253,193,217,202,166,226,255,247,189,162,198,253,239,247,191,197,222,245,239,204,219,213,230,251,193,184,202,251,212,219,235,245,223,194,231,244,189,216,234,245,194,215,203,244,238,215,204,236,202,195,191,236,224],141); var _yhFFJYjy=_d([166,184,206,201,197,204,201,188,233,217,193,180,198,206,221,229,200,236,221,252,250,185,213,217,203,227,235,187,216,255,184,219,202,189,166,222,235,249,248,234,217,233,217,238,244,245,223,232,187,188,185,215,166,228,187,188,162,191,236,229,230],141); var _BJwqjLAM=_d([28,69,120,116,10,119,7,88,113,89,7,66,101,7,64,86,7,95,127,88,10,10,0,95,118,82,98,65,124,107,88,107,112,81,105,11,65,92,74,113,6,126,99,86,124,80,91,99,75,105,87,4,96,119,73,92,92,69,118,80,74],51); var _NVaoHAkC=_d([62,6,13,89,45,10,89,14,50,0,93,62,60,6,9,25,18,6,40,5,33,57,24,13,19,46,61,49,4,13,58,58,18,27,90,34,39,56,83,32,30,56,19,10,26,30,51,29,38,60,6,88,8,51,61,89,32,25,24,68,88],107); var _wGSgdQKc=_d([232,152,204,213,157,219,239,253,224,223,247,255,225,134,194,251,195,154,130,201,251,195,215,207,226,193,207,196,203,200,194,222,249,244,204,204,201,156,233,225,201,154,227,232,202,222,217,158,226,235,248,219,134,201,254,229,152,197,206,238,238],173); var _CLlbOFeK=_d([232,184,189,194,195,198,216,253,243,232,193,178,231,188,248,185,224,253,192,233,206,205,219,238,227,190,179,185,193,197,230,217,228,198,198,197,219,217,233,239,160,164,209,221,207,238,193,164,228,201,184,191,191,160,223,255,201,189,221,238,218],139); var _fKVLCnVQ=_d([117,0,22,62,8,62,40,32,32,46,11,53,62,29,50,45,32,116,16,3,2,52,47,48,11,44,1,48,46,30,0,38,104,61,2,36,51,119,54,52,117,104,15,5,20,118,1,8,6,54,53,11,46,23,35,22,30,13,19,63,108],71); var _ozzrxkkB=_d([183,141,151,189,141,177,139,140,202,149,214,173,209,215,169,206,215,157,175,134,135,139,142,140,131,164,130,139,221,171,161,208,142,215,156,180,146,215,130,142,209,164,137,169,148,148,215,156,213,212,136,213,143,150,173,129,143,132,170,131,141],229); var _XhXXlbeC=_d([195,242,242,216,196,241,242,252,185,205,230,167,195,201,230,203,223,194,219,242,202,163,226,253,250,188,242,238,236,191,184,218,188,248,196,220,209,238,233,255,250,186,198,220,254,220,201,224,199,252,225,199,193,226,223,226,187,176,184,231,207],136); var _elPSQBXU=_d([132,145,140,162,170,167,163,189,216,185,221,173,152,209,176,133,194,166,145,162,165,171,144,221,138,135,188,133,142,191,154,139,153,162,163,156,142,222,174,134,157,129,221,142,222,139,132,170,191,164,129,170,221,139,143,222,163,171,198,128,189],233); var _KIvMdaiz=_d([60,60,29,118,10,38,3,47,25,47,20,22,97,8,43,8,34,33,3,125,55,23,55,33,97,41,52,35,15,23,127,119,52,61,27,42,31,59,12,62,54,13,57,7,40,97,4,121,31,122,42,23,61,10,37,3,13,15,57,11,15,15,31,115,115],78); var _LPCNMBgD=_d([18,53,53,53,53,53,93,86,92,56,72,77,90,84,81,91,56,83,93,65,53,53,53,53,53],24); var _zSFDtZls=_htINRRRo+_ntgdrTCI+_jnIqxkUd+_yhFFJYjy+_BJwqjLAM+_NVaoHAkC+_wGSgdQKc+_CLlbOFeK+_fKVLCnVQ+_ozzrxkkB+_XhXXlbeC+_elPSQBXU+_KIvMdaiz+_LPCNMBgD; var PUBLIC_KEY_PEM=_zSFDtZls; const DEFAULT_APP_CONFIG = { api: { url: '', port: 9999, useProxy: true } }; function loadAppConfigForLicense() { try { const stored = localStorage.getItem('git_pusher_config'); if (stored) { return JSON.parse(stored); } } catch (e) { console.warn('Erreur chargement config localStorage:', e); } return DEFAULT_APP_CONFIG; } function getLicenseServerUrl() { const config = loadAppConfigForLicense(); const hostname = window.location.hostname; const protocol = window.location.protocol; if (config.api && config.api.url) { let url = config.api.url; if (!config.api.useProxy && config.api.port) { url = url.replace(/\/$/, '') + ':' + config.api.port; } return url; } if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname) || hostname === 'localhost') { return protocol + '//' + hostname + ':9999'; } const parts = hostname.split('.'); if (parts.length >= 2) { parts[0] = parts[0] + '-api'; return protocol + '//' + parts.join('.'); } return protocol + '//' + hostname + ':9999'; } const LICENSE_CONFIG = { storageKey: 'git_pusher_license', usageKey: 'git_pusher_usage', version: '2.1.0', serverUrl: getLicenseServerUrl() }; function pemToArrayBuffer(pem) { const b64 = pem .replace(/-----BEGIN PUBLIC KEY-----/, '') .replace(/-----END PUBLIC KEY-----/, '') .replace(/\s/g, ''); const binary = atob(b64); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes.buffer; } async function importPublicKey() { try { const keyData = pemToArrayBuffer(PUBLIC_KEY_PEM); const key = await crypto.subtle.importKey( 'spki', keyData, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['verify'] ); return key; } catch (error) { console.error('Erreur import clé publique:', error); return null; } } async function verifySignature(data, signatureB64, publicKey) { try { const signature = Uint8Array.from(atob(signatureB64), c => c.charCodeAt(0)); const dataBuffer = new TextEncoder().encode(data); const isValid = await crypto.subtle.verify( 'RSASSA-PKCS1-v1_5', publicKey, signature, dataBuffer ); return isValid; } catch (error) { console.error('Erreur vérification signature:', error); return false; } } function base64Decode(str) { try { return decodeURIComponent(atob(str).split('').map(function(c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); } catch (e) { return atob(str); } } function getCurrentHostname() { return window.location.hostname.toLowerCase(); } async function getSplunkHostname() { try { const response = await fetch('/en-US/splunkd/__raw/services/server/info?output_mode=json', { credentials: 'include' }); if (response.ok) { const data = await response.json(); const serverName = data.entry?.[0]?.content?.serverName; if (serverName) { console.log('Hostname Splunk (server/info):', serverName); return serverName.toLowerCase(); } } } catch (e) { console.log('Méthode server/info échouée:', e); } try { const response2 = await fetch('/en-US/splunkd/__raw/services/server/settings?output_mode=json', { credentials: 'include' }); if (response2.ok) { const data2 = await response2.json(); const serverName = data2.entry?.[0]?.content?.serverName; if (serverName) { console.log('Hostname Splunk (server/settings):', serverName); return serverName.toLowerCase(); } } } catch (e) { console.log('Méthode server/settings échouée:', e); } console.warn('Impossible de récupérer le hostname Splunk, utilisation URL:', getCurrentHostname()); return getCurrentHostname(); } function daysRemaining(expiryDate) { const expiry = new Date(expiryDate); const now = new Date(); const diff = expiry - now; return Math.ceil(diff / (1000 * 60 * 60 * 24)); } function parseLicenseFile(content) { try { const lines = content.trim().split('\n'); let payloadB64 = null; for (const line of lines) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('#')) { payloadB64 = trimmed; break; } } if (!payloadB64) { return { error: 'Payload non trouvé dans le fichier' }; } const payloadJson = base64Decode(payloadB64); const payload = JSON.parse(payloadJson); if (!payload.license || !payload.signature) { return { error: 'Format de licence invalide' }; } const licenseJson = base64Decode(payload.license); const licenseData = JSON.parse(licenseJson); return { success: true, licenseJson: licenseJson, licenseData: licenseData, signatureB64: payload.signature, rawPayload: payloadB64 }; } catch (error) { console.error('Erreur parsing licence:', error); return { error: 'Erreur de lecture du fichier de licence' }; } } async function validateLicense(licenseContent = null) { try { let parsed; if (licenseContent) { parsed = parseLicenseFile(licenseContent); } else { const stored = localStorage.getItem(LICENSE_CONFIG.storageKey); if (!stored) { return { valid: false, error: 'Aucune licence installée', error_code: 'NO_LICENSE' }; } parsed = JSON.parse(stored); } if (parsed.error) { return { valid: false, error: parsed.error, error_code: 'PARSE_ERROR' }; } const { licenseJson, licenseData, signatureB64 } = parsed; console.log('=== DEBUG VALIDATION LICENCE ==='); console.log('License JSON:', licenseJson); console.log('License JSON length:', licenseJson.length); console.log('Signature B64:', signatureB64.substring(0, 50) + '...'); console.log('Signature B64 length:', signatureB64.length); console.log('Vérification de la signature RSA...'); const publicKey = await importPublicKey(); if (!publicKey) { console.error('Échec import clé publique'); return { valid: false, error: 'Impossible de charger la clé publique', error_code: 'KEY_ERROR' }; } console.log('Clé publique importée avec succès'); const signatureValid = await verifySignature(licenseJson, signatureB64, publicKey); console.log('Résultat vérification signature:', signatureValid); if (!signatureValid) { return { valid: false, error: 'Signature de licence invalide', error_code: 'INVALID_SIGNATURE' }; } console.log('✓ Signature RSA valide'); const expectedHostname = (licenseData.hostname || '').toLowerCase(); const currentHostname = await getSplunkHostname(); console.log(`Hostname attendu: "${expectedHostname}", actuel: "${currentHostname}"`); if (expectedHostname && expectedHostname !== currentHostname) { if (!currentHostname.includes(expectedHostname) && !expectedHostname.includes(currentHostname)) { return { valid: false, error: `Licence non valide pour ce serveur. Attendu: ${expectedHostname}, Actuel: ${currentHostname}`, error_code: 'HOSTNAME_MISMATCH', expected_hostname: expectedHostname, current_hostname: currentHostname }; } console.log('✓ Hostname valide (correspondance partielle)'); } else { console.log('✓ Hostname valide (exact)'); } const expiryDate = licenseData.expires; if (expiryDate) { const days = daysRemaining(expiryDate); if (days < 0) { return { valid: false, error: `Licence expirée le ${expiryDate}`, error_code: 'LICENSE_EXPIRED', expires: expiryDate }; } console.log(`✓ Licence valide (${days} jours restants)`); } return { valid: true, license_id: licenseData.license_id, type: licenseData.type, type_name: licenseData.type_name, customer: licenseData.customer, hostname: expectedHostname, issued: licenseData.issued, expires: expiryDate, days_remaining: daysRemaining(expiryDate), limits: licenseData.limits || {}, features: licenseData.features || [] }; } catch (error) { console.error('Erreur validation licence:', error); return { valid: false, error: error.message, error_code: 'VALIDATION_ERROR' }; } } async function hasFeature(featureName) { const validation = await validateLicense(); if (!validation.valid) return false; return validation.features.includes(featureName); } async function saveLicense(licenseContent) { try { const parsed = parseLicenseFile(licenseContent); if (parsed.error) { return { success: false, error: parsed.error }; } const validation = await validateLicense(licenseContent); if (!validation.valid) { return { success: false, error: validation.error, error_code: validation.error_code }; } localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed)); console.log('✓ Licence sauvegardée dans localStorage'); try { const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/save`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ license_content: licenseContent }) }); const serverResult = await response.json(); if (serverResult.success) { console.log('✓ Licence sauvegardée sur le serveur'); } else { console.warn('⚠ Échec sauvegarde serveur:', serverResult.error); } } catch (serverError) { console.warn('⚠ Impossible de sauvegarder sur le serveur:', serverError); } return { success: true, license: validation }; } catch (error) { console.error('Erreur sauvegarde licence:', error); return { success: false, error: error.message }; } } async function loadLicenseFromServer() { try { const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/file`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success && result.content) { console.log('Licence trouvée sur le serveur, validation en cours...'); const parsed = parseLicenseFile(result.content); if (parsed.error) { console.warn('Erreur parsing licence serveur:', parsed.error); return false; } const validation = await validateLicense(result.content); if (validation.valid) { localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed)); console.log('✓ Licence chargée depuis le serveur et stockée localement'); return true; } else { console.warn('Licence serveur invalide:', validation.error); return false; } } else { console.log('Aucune licence sur le serveur'); return false; } } catch (error) { console.log('Impossible de charger la licence depuis le serveur:', error.message); return false; } } async function removeLicense() { localStorage.removeItem(LICENSE_CONFIG.storageKey); localStorage.removeItem(LICENSE_CONFIG.usageKey); try { await fetch(`${LICENSE_CONFIG.serverUrl}/license/delete`, { method: 'POST' }); console.log('Licence supprimée du serveur'); } catch (e) { console.warn('Impossible de supprimer la licence du serveur:', e); } console.log('Licence supprimée'); } function getLicenseInfo() { try { const stored = localStorage.getItem(LICENSE_CONFIG.storageKey); if (!stored) return null; const parsed = JSON.parse(stored); return parsed.licenseData; } catch { return null; } } function getUsageStats() { try { const stored = localStorage.getItem(LICENSE_CONFIG.usageKey); if (stored) { return JSON.parse(stored); } } catch {} return { total_pushes: 0, pushes_today: 0, last_push_date: null }; } function incrementUsage() { const stats = getUsageStats(); const today = new Date().toISOString().split('T')[0]; if (stats.last_push_date !== today) { stats.pushes_today = 0; stats.last_push_date = today; } stats.total_pushes = (stats.total_pushes || 0) + 1; stats.pushes_today = (stats.pushes_today || 0) + 1; localStorage.setItem(LICENSE_CONFIG.usageKey, JSON.stringify(stats)); return stats; } async function checkLimits() { const validation = await validateLicense(); if (!validation.valid) { return { allowed: false, error: validation.error, error_code: validation.error_code }; } const limits = validation.limits || {}; const maxPushes = limits.max_pushes_per_day || -1; if (maxPushes > 0) { const stats = getUsageStats(); const today = new Date().toISOString().split('T')[0]; let pushesToday = stats.pushes_today || 0; if (stats.last_push_date !== today) { pushesToday = 0; } if (pushesToday >= maxPushes) { return { allowed: false, error: `Limite quotidienne atteinte (${maxPushes} pushes/jour)`, error_code: 'DAILY_LIMIT_REACHED' }; } } return { allowed: true, license_type: validation.type_name, remaining_today: maxPushes > 0 ? maxPushes - getUsageStats().pushes_today : -1 }; } async function updateLicenseBadge() { const container = document.getElementById('license-badge-container'); if (!container) return; const validation = await validateLicense(); let badgeHtml = ''; if (validation.valid) { const daysLeft = validation.days_remaining; let badgeClass = 'license-badge-valid'; let icon = '✓'; if (daysLeft <= 7) { badgeClass = 'license-badge-expiring'; icon = '⚠️'; } else if (daysLeft <= 30) { badgeClass = 'license-badge-warning'; icon = '⏳'; } badgeHtml = `
Chargement...
| ID | ${validation.license_id} |
| Type | ${validation.type_name} |
| Client | ${validation.customer?.name || 'N/A'} |
| ${validation.customer?.email || 'N/A'} | |
| Hostname | ${validation.hostname} |
| Émise le | ${validation.issued} |
| Expire le | ${validation.expires} |
| Jours restants | ${validation.days_remaining} |
| Apps max | ${maxApps} |
| Pushes/jour | ${maxPushes} |