diff --git a/license_validation.obfuscatedjs b/license_validation.obfuscatedjs new file mode 100644 index 00000000..4685963d --- /dev/null +++ b/license_validation.obfuscatedjs @@ -0,0 +1,971 @@ + +// GitDeploy for Splunk License Module v2.1 +// (c) 2026 - Proprietary Software + +function _d(a,k){return a.map(function(c){return String.fromCharCode(c^k)}).join('')} + +var _uJCanJCN='zf6wsK2aqrUWBYnFjC9uFDRCRyxntUam'; +var _PSVoriEP='u9NlvQmbiU68cRGTFGRTDGG52klcpHHX'; +var _VqfOoXRL='aThM6S6JjW606S6Q8PVw2MkNZoKNxoIZ'; +var _UsxHbSlw='ANNLTV7Tdw3RlkYqlWvCnbAb7CzJ7S11'; +var _nIwfGUYn='YVrhfajbZ39Mqqc68vJBIJC9333Msdjt'; +var _TAOuWYFb='00hwrohPmzSOkoTYN0N1rZ4UwpfMoAjG'; +var _foysGQea='Va2j2uYdB4eKMuOs40vfpfWyd6q8TAPk'; +var _IRAXmSbe='Tycs9NWX7QpfibpkJTYqmXZfCWm77dU8'; +var _FNLuVBne='I2POC1zJVMMxpXe89DS5IF8CAKYgUf3s'; +var _oEfNUTRY='vjX6Yb8hSg0043OcF2xyzawy3yC4KFBX'; + +function _OfHOnlYG(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _eYveueOu(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _UoGRlkcX(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _fwidaqtP(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _MSajwVYl(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + +var _FjcaQpEt=_d([212,212,212,212,212,187,188,190,176,183,217,169,172,187,181,176,186,217,178,188,160,212,212,212,212,212,243],249); +var _iaKBmiMy=_d([204,200,200,194,200,235,192,207,195,230,234,240,233,234,232,198,184,246,177,195,192,208,196,199,192,192,206,194,192,230,185,192,204,200,200,194,194,230,202,194,192,230,196,192,239,248,213,224,235,206,245,205,197,247,182,248,200,239,185,249,183],129); +var _kzrwWKuF=_d([242,232,137,180,247,166,134,246,243,167,179,172,247,170,250,164,168,236,172,165,247,174,186,139,247,161,143,176,175,243,145,172,247,187,240,139,138,160,175,173,142,129,176,179,160,241,133,171,130,137,129,148,185,176,162,141,186,133,162,154,160],195); +var _xvDWZVKC=_d([172,203,209,236,192,221,173,239,168,216,200,247,222,246,246,175,220,202,238,212,210,160,235,254,236,193,221,227,242,246,170,195,174,182,238,208,242,207,221,170,216,171,192,168,241,246,174,213,205,203,218,233,243,247,221,222,195,214,169,227,222],153); +var _dWTgAota=_d([249,222,247,244,234,206,247,209,206,138,223,250,238,238,148,239,144,214,238,200,194,243,221,249,242,235,138,143,210,236,214,131,140,211,148,144,143,235,238,211,247,223,246,218,143,136,148,195,136,207,140,235,221,220,203,244,241,217,221,214,139],187); +var _NmxrUikG=_d([242,218,139,147,251,140,151,243,201,214,230,254,206,200,245,248,228,235,254,228,197,236,233,241,250,206,229,133,233,234,203,209,143,218,239,204,143,221,234,222,215,208,234,215,206,247,221,214,218,251,151,238,143,238,217,245,212,212,147,133,255],188); +var _JUAOKSSi=_d([250,223,182,239,196,238,220,200,241,207,170,169,245,213,197,202,212,170,217,228,212,252,169,219,240,210,236,223,172,237,169,252,239,250,239,203,199,250,211,250,202,249,164,250,215,200,220,170,170,175,233,244,178,215,248,218,165,252,223,214,201],157); +var _nlMaszEP=_d([196,194,178,207,172,189,178,165,164,167,128,174,153,158,155,198,159,172,140,195,140,165,197,167,163,151,196,154,159,175,154,181,190,158,149,187,181,143,149,206,161,142,190,164,193,160,143,155,131,183,154,207,199,131,128,186,152,142,194,206,161],246); +var _PwnaiQwt=_d([25,7,16,99,52,58,56,24,6,24,120,6,20,39,33,0,103,111,53,13,27,30,102,103,17,101,19,32,27,20,101,103,6,2,1,5,19,21,19,53,39,53,103,46,21,51,24,60,99,63,61,22,35,15,99,103,0,1,5,100,28],87); +var _aotVNNKe=_d([136,183,134,157,134,168,189,252,139,253,132,244,132,176,238,176,159,135,168,163,173,182,164,172,172,176,174,246,138,172,136,135,171,150,141,178,144,181,164,166,180,238,180,138,136,131,156,141,169,129,240,141,240,141,189,177,247,252,241,179,131],197); +var _VJUKELsd=_d([149,129,244,128,134,176,152,187,171,148,176,131,165,247,140,146,146,154,245,171,180,129,237,246,179,152,135,161,167,173,154,146,163,245,161,137,142,131,150,247,136,186,147,128,187,175,163,246,233,166,134,179,181,149,245,247,161,164,172,167,135],194); +var _UpywgYkf=_d([181,165,177,241,180,146,178,133,130,171,244,190,132,190,176,146,243,140,144,178,189,136,246,160,151,128,240,183,148,174,160,180,177,147,241,168,166,130,255,146,171,190,137,150,144,172,190,143,150,137,171,142,129,144,169,189,171,151,181,157,161],199); +var _rnnGgybM=_d([229,131,151,177,235,148,226,187,161,149,253,134,129,158,156,177,144,166,136,163,184,228,150,166,162,228,182,191,153,152,159,181,226,144,138,168,189,148,180,253,166,191,253,130,151,184,161,228,253,132,226,131,188,145,158,234,145,147,165,151,147,147,131,239,239],210); +var _cOKgywUY=_d([180,147,147,147,147,147,251,240,250,158,238,235,252,242,247,253,158,245,251,231,147,147,147,147,147],190); +var _EfUWUghE=_FjcaQpEt+_iaKBmiMy+_kzrwWKuF+_xvDWZVKC+_dWTgAota+_NmxrUikG+_JUAOKSSi+_nlMaszEP+_PwnaiQwt+_aotVNNKe+_VJUKELsd+_UpywgYkf+_rnnGgybM+_cOKgywUY; +var PUBLIC_KEY_PEM=_EfUWUghE; + +const DEFAULT_APP_CONFIG = { + api: { + url: '', + port: 9999, + useProxy: true + } +}; +function loadAppConfigForLicense() { + try { + const stored = localStorage.getItem('gitdeploy_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: 'gitdeploy_license', + usageKey: 'gitdeploy_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} |