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 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.06",
1610
- updated: "2026-03-06",
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
- "webhook.site",
1625
- "requestbin.com",
1626
- "pipedream.com",
1627
- "pipedream.net",
1628
- "hookbin.com",
1629
- "beeceptor.com",
1630
- "ngrok.io",
1631
- "ngrok-free.app",
1632
- "serveo.net",
1633
- "localtunnel.me",
1634
- "bore.pub",
1635
- "interact.sh",
1636
- "oast.fun",
1637
- "oastify.com",
1638
- "dnslog.cn",
1639
- "ceye.io",
1640
- "burpcollaborator.net",
1641
- "pastebin.com",
1642
- "paste.ee",
1643
- "hastebin.com",
1644
- "ghostbin.com",
1645
- "evil.com",
1646
- "malware.com",
1647
- "exploit.in"
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
- base.malicious_domains = dedupe([
1706
- ...base.malicious_domains,
1707
- ...ext.malicious_domains
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.malicious_domains.length > 0 ? ioc.malicious_domains : FALLBACK_SUSPICIOUS_DOMAINS;
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: "CRITICAL",
1932
- title: "Suspicious domain detected",
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
  }