skill-checker 0.1.12 → 0.1.13
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/dist/cli.js +145 -37
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +145 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1606,8 +1606,8 @@ import { homedir } from "os";
|
|
|
1606
1606
|
|
|
1607
1607
|
// src/ioc/indicators.ts
|
|
1608
1608
|
var DEFAULT_IOC = {
|
|
1609
|
-
version: "2026.03.
|
|
1610
|
-
updated: "2026-03-
|
|
1609
|
+
version: "2026.03.16",
|
|
1610
|
+
updated: "2026-03-16",
|
|
1611
1611
|
c2_ips: [
|
|
1612
1612
|
"91.92.242.30",
|
|
1613
1613
|
"91.92.242.39",
|
|
@@ -1620,32 +1620,61 @@ var DEFAULT_IOC = {
|
|
|
1620
1620
|
// as it causes false positives on any empty file.
|
|
1621
1621
|
"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2": "clawhavoc-exfiltrator"
|
|
1622
1622
|
},
|
|
1623
|
-
malicious_domains:
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1623
|
+
malicious_domains: {
|
|
1624
|
+
exfiltration: [
|
|
1625
|
+
"webhook.site",
|
|
1626
|
+
"requestbin.com",
|
|
1627
|
+
"requestcatcher.com",
|
|
1628
|
+
"pipedream.com",
|
|
1629
|
+
"pipedream.net",
|
|
1630
|
+
"hookbin.com",
|
|
1631
|
+
"beeceptor.com",
|
|
1632
|
+
"postb.in",
|
|
1633
|
+
"webhook.lol",
|
|
1634
|
+
"requestinspector.com",
|
|
1635
|
+
"mockbin.org"
|
|
1636
|
+
],
|
|
1637
|
+
tunnel: [
|
|
1638
|
+
"ngrok.io",
|
|
1639
|
+
"ngrok-free.app",
|
|
1640
|
+
"serveo.net",
|
|
1641
|
+
"localtunnel.me",
|
|
1642
|
+
"bore.pub",
|
|
1643
|
+
"localhost.run",
|
|
1644
|
+
"loca.lt",
|
|
1645
|
+
"telebit.cloud",
|
|
1646
|
+
"playit.gg",
|
|
1647
|
+
"portmap.io",
|
|
1648
|
+
"pagekite.me"
|
|
1649
|
+
],
|
|
1650
|
+
oast: [
|
|
1651
|
+
"interact.sh",
|
|
1652
|
+
"oast.fun",
|
|
1653
|
+
"oastify.com",
|
|
1654
|
+
"dnslog.cn",
|
|
1655
|
+
"ceye.io",
|
|
1656
|
+
"burpcollaborator.net",
|
|
1657
|
+
"canarytokens.com",
|
|
1658
|
+
"requestrepo.com"
|
|
1659
|
+
],
|
|
1660
|
+
paste: [
|
|
1661
|
+
"pastebin.com",
|
|
1662
|
+
"paste.ee",
|
|
1663
|
+
"hastebin.com",
|
|
1664
|
+
"ghostbin.com",
|
|
1665
|
+
"dpaste.org",
|
|
1666
|
+
"rentry.co",
|
|
1667
|
+
"0bin.net",
|
|
1668
|
+
"privatebin.net",
|
|
1669
|
+
"paste.mozilla.org"
|
|
1670
|
+
],
|
|
1671
|
+
c2: [
|
|
1672
|
+
"evil.com",
|
|
1673
|
+
"malware.com",
|
|
1674
|
+
"exploit.in",
|
|
1675
|
+
"darkweb.onion"
|
|
1676
|
+
]
|
|
1677
|
+
},
|
|
1649
1678
|
typosquat: {
|
|
1650
1679
|
known_patterns: [
|
|
1651
1680
|
"clawhub1",
|
|
@@ -1702,10 +1731,21 @@ function mergeIOC(base, ext) {
|
|
|
1702
1731
|
Object.assign(base.malicious_hashes, ext.malicious_hashes);
|
|
1703
1732
|
}
|
|
1704
1733
|
if (ext.malicious_domains) {
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1734
|
+
const categories = [
|
|
1735
|
+
"exfiltration",
|
|
1736
|
+
"tunnel",
|
|
1737
|
+
"oast",
|
|
1738
|
+
"paste",
|
|
1739
|
+
"c2"
|
|
1740
|
+
];
|
|
1741
|
+
for (const cat of categories) {
|
|
1742
|
+
if (ext.malicious_domains[cat]) {
|
|
1743
|
+
base.malicious_domains[cat] = dedupe([
|
|
1744
|
+
...base.malicious_domains[cat],
|
|
1745
|
+
...ext.malicious_domains[cat]
|
|
1746
|
+
]);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1709
1749
|
}
|
|
1710
1750
|
if (ext.typosquat) {
|
|
1711
1751
|
if (ext.typosquat.known_patterns) {
|
|
@@ -1730,6 +1770,22 @@ function mergeIOC(base, ext) {
|
|
|
1730
1770
|
if (ext.version) base.version = ext.version;
|
|
1731
1771
|
if (ext.updated) base.updated = ext.updated;
|
|
1732
1772
|
}
|
|
1773
|
+
function getAllDomains(ioc) {
|
|
1774
|
+
const { exfiltration, tunnel, oast, paste, c2 } = ioc.malicious_domains;
|
|
1775
|
+
return [...exfiltration, ...tunnel, ...oast, ...paste, ...c2];
|
|
1776
|
+
}
|
|
1777
|
+
function getDomainCategory(ioc, domain) {
|
|
1778
|
+
const d = domain.toLowerCase();
|
|
1779
|
+
const categories = ["exfiltration", "tunnel", "oast", "paste", "c2"];
|
|
1780
|
+
for (const cat of categories) {
|
|
1781
|
+
if (ioc.malicious_domains[cat].some(
|
|
1782
|
+
(entry) => d === entry || d.endsWith("." + entry)
|
|
1783
|
+
)) {
|
|
1784
|
+
return cat;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
return void 0;
|
|
1788
|
+
}
|
|
1733
1789
|
function dedupe(arr) {
|
|
1734
1790
|
return [...new Set(arr)];
|
|
1735
1791
|
}
|
|
@@ -1738,6 +1794,24 @@ function dedupe(arr) {
|
|
|
1738
1794
|
var FALLBACK_SUSPICIOUS_DOMAINS = [
|
|
1739
1795
|
"darkweb.onion"
|
|
1740
1796
|
];
|
|
1797
|
+
function isSensitiveDomainCombo(line) {
|
|
1798
|
+
if (/curl\b[^\n]*(?:-d|--data|--data-binary|--data-raw|--data-urlencode)\s+@/i.test(line)) {
|
|
1799
|
+
return true;
|
|
1800
|
+
}
|
|
1801
|
+
if (/curl\b[^\n]*(?:-F|--form)\s+[^\s=]+=@/i.test(line)) {
|
|
1802
|
+
return true;
|
|
1803
|
+
}
|
|
1804
|
+
if (/wget\b[^\n]*--post-file/i.test(line)) {
|
|
1805
|
+
return true;
|
|
1806
|
+
}
|
|
1807
|
+
if (/\|\s*(?:sh|bash|zsh|python|node)\b/i.test(line)) {
|
|
1808
|
+
return true;
|
|
1809
|
+
}
|
|
1810
|
+
if (/(?:\.env|\.ssh|id_rsa|\.aws|credentials|\.netrc|\.git-credentials)/i.test(line)) {
|
|
1811
|
+
return true;
|
|
1812
|
+
}
|
|
1813
|
+
return false;
|
|
1814
|
+
}
|
|
1741
1815
|
var MCP_SERVER_PATTERN = /\bmcp[-_]?server\b/i;
|
|
1742
1816
|
var NPX_Y_PATTERN = /\bnpx\s+-y\s+/;
|
|
1743
1817
|
var NPM_INSTALL_PATTERN = /\bnpm\s+install\b/;
|
|
@@ -1762,7 +1836,10 @@ var supplyChainChecks = {
|
|
|
1762
1836
|
const results = [];
|
|
1763
1837
|
const allText = getAllText(skill);
|
|
1764
1838
|
const ioc = loadIOC();
|
|
1765
|
-
const suspiciousDomains = ioc
|
|
1839
|
+
const suspiciousDomains = getAllDomains(ioc);
|
|
1840
|
+
if (suspiciousDomains.length === 0) {
|
|
1841
|
+
suspiciousDomains.push(...FALLBACK_SUSPICIOUS_DOMAINS);
|
|
1842
|
+
}
|
|
1766
1843
|
for (let i = 0; i < allText.length; i++) {
|
|
1767
1844
|
const { line, lineNum, source } = allText[i];
|
|
1768
1845
|
if (MCP_SERVER_PATTERN.test(line)) {
|
|
@@ -1925,15 +2002,46 @@ var supplyChainChecks = {
|
|
|
1925
2002
|
const hostname = extractHostname(url);
|
|
1926
2003
|
for (const domain of suspiciousDomains) {
|
|
1927
2004
|
if (hostnameMatchesDomain(hostname, domain)) {
|
|
2005
|
+
const category = getDomainCategory(ioc, domain);
|
|
2006
|
+
const categoryLabel = category ? ` (${category})` : "";
|
|
2007
|
+
let severity = "HIGH";
|
|
2008
|
+
let reducedFrom;
|
|
2009
|
+
let msgSuffix = "";
|
|
2010
|
+
if (isSensitiveDomainCombo(line)) {
|
|
2011
|
+
severity = "CRITICAL";
|
|
2012
|
+
msgSuffix = " [escalated: combined with sensitive operation]";
|
|
2013
|
+
} else {
|
|
2014
|
+
const srcLines = getLinesForSource(skill, source);
|
|
2015
|
+
const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);
|
|
2016
|
+
const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);
|
|
2017
|
+
if (inCodeBlock) {
|
|
2018
|
+
severity = "MEDIUM";
|
|
2019
|
+
reducedFrom = "HIGH";
|
|
2020
|
+
msgSuffix = " [reduced: in code block]";
|
|
2021
|
+
} else {
|
|
2022
|
+
const allLines = getAllLines(skill);
|
|
2023
|
+
const globalIdx = findGlobalLineIndex(allLines, source, lineNum);
|
|
2024
|
+
const isDoc = source === "SKILL.md" && globalIdx >= 0 && isInDocumentationContext(
|
|
2025
|
+
allLines.map((l) => l.line),
|
|
2026
|
+
globalIdx
|
|
2027
|
+
);
|
|
2028
|
+
if (isDoc) {
|
|
2029
|
+
severity = "LOW";
|
|
2030
|
+
reducedFrom = "HIGH";
|
|
2031
|
+
msgSuffix = " [reduced: in documentation context]";
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
1928
2035
|
results.push({
|
|
1929
2036
|
id: "SUPPLY-007",
|
|
1930
2037
|
category: "SUPPLY",
|
|
1931
|
-
severity
|
|
1932
|
-
title:
|
|
1933
|
-
message: `${source}:${lineNum}: References suspicious domain "${domain}"
|
|
2038
|
+
severity,
|
|
2039
|
+
title: `Suspicious domain${categoryLabel} detected`,
|
|
2040
|
+
message: `${source}:${lineNum}: References suspicious domain "${domain}".${msgSuffix}`,
|
|
1934
2041
|
line: lineNum,
|
|
1935
2042
|
snippet: url,
|
|
1936
|
-
source
|
|
2043
|
+
source,
|
|
2044
|
+
reducedFrom
|
|
1937
2045
|
});
|
|
1938
2046
|
break;
|
|
1939
2047
|
}
|