QuietPolyfill, un dropper NPM care a reînviat

QuietPolyfill: Un dropper NPM care a readus la viață

TL; DR

Douăzeci de pachete npm publicate pe parcursul a două zile la sfârșitul lunii iulie și începutul lunii august 2026 se prezintă ca biblioteci interne de componente UI și utilitare backend partajate. Niciunul dintre ele nu implementează funcționalitatea pe care o promovează. Fiecare conține o clasă stub, un fișier README care descrie pachetul ca fiind intern și un fișier numit _polyfill.js.

Partea interesantă este de unde este invocat acel fișier. Nu este un hook de instalare — manifestele nu declară nicio preinstalare, instalare sau postinstalare. În schimb, ultima linie a index.js prevede: try { require("./_polyfill"); } catch (_) {}Sarcina utilă rulează prima dată când codul aplicației importă pachetul. Instalare cu --ignore-scripts, standard sfaturi pentru reducerea expunerii lanțului de aprovizionare în momentul instalării, nu o împiedică.

Ce _polyfill.js Ceea ce face este să preia un fișier executabil specific platformei de la una dintre cele trei gazde Cloudflare Workers, să îl scrie într-un director temporar sub un nume aleatoriu, să îl marcheze ca executabil și să îl lanseze detașat de procesul părinte. Executabilul respectiv este el însuși un încărcător: enumeră produsele de securitate endpoint după numele procesului, verifică numele de utilizator curent în raport cu conturile de mediu de analiză cunoscute, aplică patch-uri pentru interfețele de telemetrie AMSI și ETW în memorie, instalează persistența sub numele unei componente audio Windows și preia un al treilea fișier binar de câțiva megaocteți. A treia etapă ajunge criptată și este... decriptat și executat în memorie, nu scris pe disc.

Pe 1 august, la aproximativ patru ore după ce pachetele au fost confirmate ca fiind malițioase în urma unei verificări manuale, nouăsprezece dintre cele douăzeci de nume au fost eliminate de pe npm de către propriul editor într-o secvență de douăzeci și cinci de secunde. Pe 2 august, unul dintre aceste nume a fost înregistrat din nou - de către un alt cont, purtând același cod. Infrastructura de livrare deservea toate cele trei etape la momentul redactării acestui document.

Pachete20 de nume npm, ~74 de versiuni publicate
Denumirea temelorbeaver-ui-* (13), accounts-* (3), plus afisha-storybook-default, bcore-bravo-eslint-config, arbocrate-sla-prober-arbocrate-sla-prober-core, a.poltoradnev-package-c
trăgaciImportul modulului (require), nu un script al ciclului de viață npm
Etapă 1_polyfill.js — recuperare și lansare
Etapă 2Încărcător static, încărcat cu UPX pe Linux; enumerare a securității endpoint-urilor, aplicarea de patch-uri AMSI/ETW, persistență
Etapă 3beacon_linux.bin / beacon_mac.bin / update_win.exe, 4–13 MB; livrat criptat, executat în memorie
PlatformeLinux x64 și arm64, macOS universal, Windows x64
EditoriiDouă conturi, o singură bază de cod
Stare19 din 20 de nume nepublicate de către operator; 1 republicat și activ; infrastructura de livrare activă

Anatomia atacului

Etapa 1 — pachetul

Fiecare membru livrează aceeași formă cu trei fișiere. index.js exportă o clasă cu un init () care setează un boolean, un versiune(), Precum și o configure() care îmbină un obiect de opțiuni. Nimic altceva. README.md oferă o linie de instalare, o necesita linie și propoziția „Pachet intern. Consultați CONTRIBUTING.md pentru instrucțiuni” — CONTRIBUTING.md nu se află în tarball.

_polyfill.js are puțin sub 5 KB și se citește ca cod de infrastructură obișnuit. Se deschide cu trei verificări de mediu:

if (process.env.DISABLE_TELEMETRY ||
    process.env.ANALYTICS_OPT_OUT ||
    process.env.DO_NOT_TRACK)
  return;

Aceste variabile sunt controale convenționale de confidențialitate. Aici, ele controlează dacă un fișier binar este descărcat și executat. O mașină care setează NU_URMĂRIȚI este omisă în întregime.

Urmează o ștampilă rulabilă o singură dată: un fișier la /tmp/.analytics_state, stare_analitică în % TEMP% pe Windows, al cărui timp de modificare este comparat cu o fereastră de 20,357 de secunde. În cadrul acestei ferestre, modulul revine imediat, astfel încât importurile repetate în timpul unei construcții produc o singură încercare de recuperare în loc de zeci.

Gazdele destinație nu sunt prezente ca literali. Acestea sunt asamblate în timpul execuției din fragmente:

["oob-worker.cf102-","baf.workers.d","ev"].join("") 

Trei gazde sunt construite în acest fel și amestecate înainte de utilizare, astfel încât ordinea variază în funcție de execuție. Recuperarea este un HTTPS GET fixat la IPv4 cu un Agent de utilizator of node-fetch/2.6 — o valoare plauzibilă pe care o poate emite un proces Node. Un răspuns sub 1,000 de octeți este eliminat și se încearcă următoarea gazdă.

Dacă toate trei eșuează, modulul revine la DNS. Acesta emite o interogare TXT pentru c. pentru a obține un număr de fragmente, apoi solicită 0. prin n. în loturi de zece, concatenează răspunsurile și decodează rezultatul în base64. Această cale transportă același executabil peste înregistrările TXT DNS, care supraviețuiește mediilor în care HTTP-ul de ieșire este filtrat, dar rezoluția DNS nu este.

Octeții recuperați sunt scriși în /var/tmp/.cache_<8-hex> pe Unix sau %TEMP%\dotnet_diag_<8-hex>.exe pe Windows — un nume de fișier care se citește ca un artefact de diagnosticare .NET. Pe Unix, fișierul are modul 0755. Lansarea este icre implementate cu detașat: adevărat, stdio: „ignorare” și unref(), deci copilul supraviețuiește procesului Node care l-a inițiat; pe Windows, invocarea adaugă Ascundeți ferestrele: adevărat.

Două detalii completează imaginea. Un ajutor definit ca const _l = (…a) => proces.stderr.write(“”) este apelat la fiecare pas al regăsirii — scrie un șir gol și își elimină argumentele, ocupând poziția în care s-ar afla jurnalizarea. Și fiecare pachet livrează un fișier de 81 KB la lib/telemetry.js al cărui comentariu în antet descrie un „SDK Analytics” cu „transport HTTPS cu rotație endpoint și reîncercare” și „descoperire de servicii bazate pe DNS pentru failover”. În versiunea actuală, nimic nu necesită acest lucru.

Etapa 2 — încărcătorul

Fișierul binar Linux x64 este un ELF cu link-uri statice comprimat cu UPX 3.96. Versiunea macOS este un binar universal care acoperă x86_64 și arm64; versiunea Windows este un executabil de consolă PE32+ simplificat. Toate cele patru au fost recuperate și examinate static; niciunul nu a fost executat.

Importurile și tabelul de șiruri ale versiunii Windows descriu clar comportamentul acesteia. Rezolvă IsDebuggerPresent, VerificareDepanatorLaDistanțăPrezent și NtQueryInformationProcess, și citește SYSTEM\CurrentControlSet\Services\Disk\Enum — o cale de registry ale cărei valori identifică controlerele de disc virtuale. Aceasta apelează GetDiskFreeSpaceExW și StareMemorieGlobalăEx, în concordanță cu verificarea dacă resursele gazdei corespund unei instanțe de analiză de scurtă durată.

Conține o listă de nume de utilizator: Utilizator curent, Administrator IT, Hong Lee, TESTBOX, hapubws, milozs, timmy, admin, malware, probă, virus, mediu inconjuratorMai multe dintre acestea sunt artefacte ale unor medii de analiză documentate public.

De asemenea, conține o listă de produse de securitate endpoint și numele proceselor acestora, enumerate prin CreateToolhelp32Snapshot și Process32FirstW/Process32NextW:

  • CrowdStrike Falcon (csfalconservice, CSFalconContainer)
  • Negru de fum (cb.exe)
  • Cylance (cylancesvc.exe)
  • Bitdefender (bdagent.exe)
  • Kaspersky (avp.exe, kavfs.exe)
  • Trend Micro și Deep Security (coreServiceShell, ds_agent.exe)
  • FireEye/Trellix (xagt.exe)
  • Tanium (taniumclient.exe)
  • Hitmanpro (hmpalert.exe)
  • Elastic (elastic-agent.exe, elastic-endpoint, filebeat.exe, winlogbeat.exe)
  • Wazuh și OSSEC (wazuh-agent.exe, ossec-agent.exe)
  • Check Point
  • Windows Defender

Sunt prezente încă două simboluri: Tampon AmsiScan, punctul de intrare pe care Windows îl expune pentru scanarea conținutului în memorie și EtwEventWrite, funcția prin care Event Tracing for Windows înregistrează evenimente. Ambele sunt ținte comune pentru modificările în timpul procesului care suprimă telemetria pe care altfel ar produce-o acele interfețe.

Persistența apare de două ori pe Windows. Fișierul binar este copiat în %APPDATA%\Microsoft\Windows\Themes\SystemSoundsService.exe, și două mecanisme îl fac referire: o valoare sub SOFTWARE\Microsoft\Windows\Versiune curentă\Executareși o sarcină programată înregistrată ca

schtasks /Create /F /SC ONLOGON
  /TN "\Microsoft\Windows\Multimedia\SystemSoundsService"
  /TR "\"%s\""
  /RL HIGHEST

Calea activității, numele activității și numele executabilului reflectă o componentă audio Windows autentică. /RL CEL MAI ÎNALT solicită cel mai înalt nivel de privilegii disponibil.

Versiunea macOS urmează același model cu nume adecvate platformei: scrie o listă de proprietăți pentru ~/Bibliotecă/LaunchAgents/com.apple.windowserver.helper.plist și plasează executabilul său la ~/.local/share/runtime/com.apple.runtime. Se verifică pentru /Aplicații/VMware Fusion.app și /Bibliotecă/Asistență aplicații/Instrumente VMware.

Capacitatea rețelei se întinde pe trei tipuri de transport: WININET.dll pentru HTTPS cu un https://%s:%d%s șir de format, WS2_32.dll prize și DNSAPI.dll implementate cu DnsQuery_A alături de a c.%s.%s șir de format — aceeași formă de interogare de tip chunk-count utilizată de prima etapă.

Descrierea de mai sus este ceea ce arată importurile, șirurile și căile de registry ale fișierului binar la inspecție. Analiza publică multi-motor a aceluiași exemplu Windows adaugă detalii pe care o citire statică a unui tabel de import nu le poate stabili singură și este atribuită aici, mai degrabă decât reformulată ca observație directă. Această analiză arată că referințele AMSI și ETW nu sunt doar rezolvate, ci și corectate. Tampon AmsiScan in amsi.dll și EtwEventWrite in Ntdll.dll — care suprimă scanarea conținutului în memorie și urmărirea evenimentelor pentru proces. Raportează verificările anti-analiză ca fiind bazate pe temporizare, pe lângă VerificareDepanatorLaDistanțăPrezentși identifică ObțineContextulFirului apel ca o verificare a punctului de întrerupere hardware care citește registrele de depanare. De asemenea, raportează un mutex cu o singură instanță, Global\{7A3B9E1F-C2D4-4F8A-B5E6-D7A1F0C3E2B9}și nume ServiciuSuneteSistem ca valoare a cheii de execuție. Această valoare a mutexului nu se număra printre șirurile recuperate aici — filtrul de extracție utilizat a exclus token-urile hexadecimale majuscule între acolade — și este înregistrată în tabelul de indicatori pe baza acelei analize publice, mai degrabă decât pe baza observării directe.

Etapă 3

Încărcătorul face referire la un set suplimentar de căi pe aceleași gazde. Toate au fost accesibile în timpul analizei: beacon_linux.bin la 5.3 MB, beacon_mac.bin la 13.3 MB, update_win.exe la 10.3 MB și arm.bin la 4.3 MB. Acestea nu au fost recuperate, iar comportamentul lor este necunoscut.

Pentru calea Windows, analiza publică a încărcătorului descrie modul în care este gestionată a treia etapă: este preluată prin WinINet, ajunge criptată și este decriptată în memorie și executată reflexiv — memorie alocată, importuri rezolvate, relocări procesate și controlul transferat către un nou fir de execuție prin CreareFirDin acest motiv, a treia etapă nu este niciodată scrisă în sistemul de fișiere, așa că detectarea bazată pe fișiere și analizele criminalistice ale discului post-incident nu au nimic de recuperat din aceasta. De asemenea, înseamnă că recuperarea octeților de la gazda de livrare ar genera text cifrat, nu un executabil analizabil.

 

Companiei

Toate marcajele temporale sunt UTC și provin din metadatele registrului npm, care înregistrează crearea și anularea publicării pachetelor independent de orice observator.

Atunci când Ce
2026-07-31 09:25:05 → 09:29:56 Zece nume create, fiecare începând cu versiunea 0.0.1, la un interval de aproximativ treisprezece secunde.
2026-08-01 09:38 → 11:29 Numele existente cresc prin numere de versiune umflate, 6.1.9 prin 6.1.12, 9.9.9 prin 9.9.12, 8.9.3 prin 8.9.6, 5.4.6 prin 5.4.9.
2026-08-01 13:04:14 → 13:04:18 Nouă în plus beaver-ui-* nume create într-o rafală de patru secunde, fiecare publicată direct la o versiune 12.x fără istoric anterior.
2026-08-01 ~13:00–14:30 Versiuni clasificate ca rău intenționate în urma revizuirii manuale.
2026-08-01 17:39:49 → 17:40:14 Nouăsprezece dintre cele douăzeci de nume au fost nepublicate în douăzeci și cinci de secunde, secvențial, la un interval de aproximativ 1.2 secunde.
2026-08-02 18:58:41 a.poltoradnev-package-c creat din nou sub un alt cont de întreținător, versiune 0.0.2.
2026-08-02 19:20:28 Versiune 33.9.5 publicată cu același nume.
2026-08-02 20:14 Toate cele trei gazde de livrare deservesc etapele doi și trei; DNS-ul de rezervă răspunde cu numărătoarea de blocuri.

Două dintre aceste intrări merită să ne oprim asupra lor.

Rafala de la 13:04 a plasat nouă pachete în registru în patru secunde, fiecare cu o singură versiune în cele douăsprezece și niciuna 0.0.1 Numerele de versiune de la această scară, pentru nume vechi de o zi, nu descriu un istoric al lansărilor.

Secvența de depublicare este la fel de mecanică: nouăsprezece nume, douăzeci și cinci de secunde, spațiere egală. npm înregistrează acestea ca evenimente obișnuite de depublicare, mai degrabă decât ca provizoriu lăsat în urmă de o eliminare inițiată de registru, ceea ce plasează acțiunea la nivelul contului de publicare, nu la nivelul registrului.

 

Indicatori de compromis

Gazdele și căile sunt dezactivate. Nu le rezolvați și nu le recuperați dintr-un sistem de producție.

Tip Valoare
Gazdă de livrare oob-worker[.]cf99-9b3[.]workers[.]dev
Gazdă de livrare oob-worker[.]cf102-baf[.]workers[.]dev
Gazdă de livrare oob-worker[.]cf103-070[.]workers[.]dev
Transport DNS tin[.]dl[.]well1[.]site (Linux x64), tina[.] (arm64), ldr[.] (MacOS), win[.] (Windows) — număr de fragmente la c.<domain>, date la <n>.<domain>
Căile din etapa a 2-a /pkg/package, /pkg/package-arm64, /pkg/loader_mac, /pkg/package.exe
Căile din etapa a 3-a /pkg/beacon_linux.bin, /pkg/beacon_mac.bin, /pkg/update_win.exe, /pkg/arm.bin
Calea de plasare (Unix) /var/tmp/.cache_<8-hex>
Calea de plasare (Windows) %TEMP%\dotnet_diag_<8-hex>.exe
Persistență (Windows) %APPDATA%\Microsoft\Windows\Themes\SystemSoundsService.exesarcină \Microsoft\Windows\Multimedia\SystemSoundsServiceValoarea cheii de execuție SOFTWARE\Microsoft\Windows\CurrentVersion\Run\SystemSoundsService
Mutex (Windows) Global\{7A3B9E1F-C2D4-4F8A-B5E6-D7A1F0C3E2B9}
Persistență (macOS) ~/Library/LaunchAgents/com.apple.windowserver.helper.plist; binar la ~/.local/share/runtime/com.apple.runtime
Ștampilă cu rulare unică /tmp/.analytics_state, %TEMP%\analytics_state (fereastră de 20,357 secunde)
Solicitați semnătura HTTPS GET, IPv4-pinned, User-Agent: node-fetch/2.6
Fișierul Etapa 1 _polyfill.js, SHA-256 392dc9ec85042ce1796bfa9b8c7bf6e05c7710579aab8b7b1eb73788683883af
Fișier nereferit lib/telemetry.js, SHA-256 2266c9b8e199daebe31a9afcb61d9b0d76706b63c92312fc8223d22de42e63c7
Etapa 2 (Linux x64) SHA-256 7e486657f30594afda379b97030252a09a19fe8055e25c9e371544f59bd8e9e3
Etapa 2 (Linux arm64) SHA-256 aafc37589c0d21fb729761a1579ca3b278b95bce152aacfa29243ffd27f5eeb7
Etapa 2 (macOS) SHA-256 c214746c74cae8ece8bdaf69aa05da4db6ce013f9e77452d1eed1a002fd9ba00
Etapa 2 (Windows) SHA-256 d632c2c0c7801c3da57cf849fe4b4ca8351d9a7fe73c31050bf5dbcc96a16791

Numele pachetelor

sertar beaver-ui-drawer buton-acțiuni-beaver-ui beaver-ui-card-mare beaver-ui-selector-interval-de-date beaver-ui-form beaver-ui-form-modal beaver-ui-form-object beaver-ui-grid beaver-ui-header castor-ui-hooks beaver-ui-icon-lock beaver-ui-items-with-more beaver-ui-layout conturi-formă-finală stare-încărcare-conturi cronologie-conturi afisha-storybook-implicit bcore-bravo-eslint-config arbocrate-sla-prober-arbocrate-sla-prober-core a.poltoradnev-package-c

Atribuire și comportament observat

Sunt implicate două conturi npm. Cele nouăsprezece nume nepublicate au fost publicate de un singur cont; a.poltoradnev-package-c a fost reînregistrat pe 2 august sub un nume de cont diferit și un domeniu de e-mail diferit. Codul are aceeași linie în ambele cazuri, iar a doua înregistrare conține aceleași gazde de livrare, aceleași domenii DNS de rezervă, aceeași construcție de cale de debarcare și aceeași ștampilă de execuție unică.

Perechea republicată arată și cum a fost asamblată. Versiuni 0.0.2 și 33.9.5 sunt identice la octeți, cu excepția șirului de versiune din pachet.json, Şi index.js expediat ca 0.0.2 declară const VERSIUNE = „33.9.5”Ambele artefacte au fost tăiate dintr-un arbore sursă și numerotate ulterior. Nu există 0.0.2 istoricul dezvoltării pentru 0.0.2 eliberare pentru a reprezenta.

Numerotarea versiunilor pentru întregul set urmează aceeași logică. Numele pachetelor din această campanie sunt interpretate ca lucruri care ar exista în interiorul unei organizații, mai degrabă decât într-un registru public: un set de componente UI, o configurație ESLint, un prober SLA, un pachet numit după o persoană. Când o versiune rezolvă o dependență după nume și consultă un registru public alături de unul privat, numărul de versiune mai mare este cel care câștigă. Un nume creat la 0.0.1 luni și purtând 12.9.6 marți este poziționat pentru acea comparație.

Cele două valuri de publicare diferă și într-un mod care sugerează iterație. Codul anterior a denumit fișierul său de sarcină utilă setup.js și a listat cinci gazde de livrare; fișierul curent este numit _polyfill.js și listează trei. Versiunea anterioară a eliminat fișierul șters la cinci secunde după lansare, lăsând procesul să ruleze dintr-un inode șters; versiunea actuală nu o face. Fișierul nereferit lib/telemetry.js rămâne în arhiva tar pentru ambele, este încă livrat, nu mai este conectat.

O observație despre întrerupătoarele de închidere merită menționată înaintecisely, pentru că contravine unei interpretări comune. DISABLE_TELEMETRY, ANALYTICS_OPT_OUT și NU_URMĂRIȚI sunt respectate și sunt verificate înainte de a se întâmpla orice altceva. Un cercetător al cărui mediu de analiză setează oricare dintre ele observă un pachet care nu face absolut nimic.

Impactul și ce pot face apărătorii

Consecința practică a declanșatorului în momentul importului este limitată și merită menționată separat. npm install –ignore-scripts împiedică rularea scripturilor ciclului de viață și este un sfat util. Nu are niciun efect aici, deoarece acest cod nu utilizează un script ciclului de viață. Sarcina utilă rulează atunci când codul aplicației, o rulare de test, un bundler sau un linter importă mai întâi modulul. Pe un runner CI care instalează și apoi se construiește imediat, cele două sunt la câteva secunde distanță.

Pentru vânătoare, semnalele durabile sunt structurale mai degrabă decât literale. Gazdele, numele fișierului și căile de plasare se vor schimba; aceste forme sunt mai greu de modificat:

  • Un punct de intrare în modul a cărui instrucțiune finală este un încercați { require("./x"); } catch (_) {} în jurul unui fișier care nu face parte din API-ul exportat. Un pachet cu un polyfill autentic îl importă în partea de sus și folosește rezultatul acestuia. Ignorarea fiecărei erori dintr-un import de la sfârșitul fișierului nu este considerată tratare a erorilor.
  • Șiruri gazdă asamblate de .join(“”) dintr-o serie de fragmente. Codul legitim nu are niciun motiv să împartă un nume de gazdă între elementele unui tablou.
  • Un pachet ale cărui dependențe declarate sunt goale, dar care ajunge la https, dns și proces_copil la momentul importului. Recuperarea plus execuția într-un pachet care promovează o componentă UI reprezintă întreaga descoperire.
  • Numere de versiune inconsistente cu vechimea unui pachet. Un nume publicat pentru prima dată acum câteva zile la o versiune majoră de două cifre, fără lansări intermediare, descrie altceva decât dezvoltarea sa.
  • Scrie către / var / tmp or % TEMP% urmată de a chmod până la 07:55 și o apariție detașată. Secvența este scurtă, ordonată și rareori benignă împreună.

Pe gazdele Windows există două verificări ieftine, de mare încredere. Artefactele de persistență sunt șiruri fixe și niciunul dintre ele nu aparține unui sistem curat: o sarcină programată la \Microsoft\Windows\Multimedia\SystemSoundsService, o valoare a cheii de execuție numită ServiciuSuneteSistemși un executabil cu acel nume sub %APPDATA%\Microsoft\Windows\Themes\Serviciul audio Windows autentic nu se află în niciunul dintre aceste locuri. În al doilea rând, încărcătorul folosește un mutex fix cu o singură instanță, Global\{7A3B9E1F-C2D4-4F8A-B5E6-D7A1F0C3E2B9}; prezența sa pe o gazdă este un indicator și, deoarece încărcătorul tratează un mutex existent ca fiind „deja în funcțiune”, precrearea acestuia este o inoculare viabilă pe termen scurt în timp ce este programată remedierea.

Detectarea în a treia etapă este o problemă mai dificilă și merită clarificată. Conform analizei publice, aceasta este decriptată și mapată în memorie, în loc să fie scrisă pe disc, astfel încât scanarea fișierelor și analizele criminalistice ale discului nu o vor detecta. Ceea ce rămâne observabil este amprenta proprie a încărcătorului - intrările de persistență, mutexul și cererile de ieșire către gazdele de livrare sau domeniile DNS-TXT.

Pentru mediile care rezolvă dependențele atât dintr-un registru privat, cât și dintr-unul public, atenuarea rămâne neschimbată de orice altceva de aici: se definesc numele interne astfel încât un nume public să nu le poată satisface și se fixează rezoluția în registrul intern pentru acele domenii de aplicare. Comportamentul de inflație a versiunilor descris mai sus funcționează numai acolo unde o versiune publică superioară poate câștiga un nume.

În cele din urmă, depublicarea merită citită cu atenție, nu ca un final. Nouăsprezece nume au părăsit registrul în douăzeci și cinci de secunde, iar unul a revenit o zi mai târziu sub un cont nou, infrastructura de livrare nefiind afectată pe tot parcursul. Eliminarea pachetelor nu înseamnă eliminarea operațiunii; gazdele au continuat să deservească fiecare etapă în timp ce acest lucru a fost scris. Orice arbore de dependențe care a rezolvat unul dintre aceste nume între 31 iulie și 1 august ar trebui tratat ca având oportunitatea de a executa prima etapă, indiferent dacă numele mai există sau nu.

Referinte

sca-tools-software-instrumente-de-analiză-a-compoziției
Prioritizați, remediați și securizați riscurile software
Obține-ți contul gratuit.
Nu este necesar un card de credit.

Securizează-ți dezvoltarea și livrarea de software

cu suita de produse Xygeni