skill-checker 0.1.12 → 0.1.14
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/README.md +3 -3
- package/dist/cli.js +242 -38
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +242 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Security checker for Claude Code skills — detect injection, malicious code, an
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **56 security rules** across 6 categories: structural validity, content quality, injection detection, code safety, supply chain, and resource abuse
|
|
8
8
|
- **Scoring system**: Grade A–F with 0–100 score
|
|
9
9
|
- **Dual entry**: CLI tool + PreToolUse hook for automatic interception
|
|
10
10
|
- **Configurable policies**: strict / balanced / permissive approval strategies
|
|
@@ -14,7 +14,7 @@ Security checker for Claude Code skills — detect injection, malicious code, an
|
|
|
14
14
|
|
|
15
15
|
## Security Standard & Benchmark
|
|
16
16
|
|
|
17
|
-
Skill Checker's
|
|
17
|
+
Skill Checker's 56 rules are aligned with established security frameworks including OWASP Top 10 for LLM Applications (2025), MITRE CWE, and MITRE ATT&CK. The tool ships with a reproducible benchmark dataset of six fixture skills covering all rule categories. This alignment is an internal mapping exercise — Skill Checker does not claim third-party certification or external audit status.
|
|
18
18
|
|
|
19
19
|
See [docs/SECURITY_BENCHMARK.md](docs/SECURITY_BENCHMARK.md) for the full rule mapping matrix, benchmark methodology, scoring model, and known limitations.
|
|
20
20
|
|
|
@@ -164,7 +164,7 @@ Config is resolved in order: CLI `--config` flag → project directory (walks up
|
|
|
164
164
|
|----------|-------|---------|
|
|
165
165
|
| Structural (STRUCT) | 8 | Missing SKILL.md, invalid frontmatter, binary files |
|
|
166
166
|
| Content (CONT) | 7 | Placeholder text, lorem ipsum, promotional content |
|
|
167
|
-
| Injection (INJ) |
|
|
167
|
+
| Injection (INJ) | 10 | Zero-width chars, prompt override, tag injection, social engineering, encoded payloads |
|
|
168
168
|
| Code Safety (CODE) | 15 | eval/exec, shell execution, reverse shell, data exfiltration, API key leakage, rm -rf, obfuscation |
|
|
169
169
|
| Supply Chain (SUPPLY) | 10 | Unknown MCP servers, suspicious domains, malicious hashes, typosquat |
|
|
170
170
|
| Resource Abuse (RES) | 6 | Unrestricted tool access, disable safety checks, ignore project rules |
|
package/dist/cli.js
CHANGED
|
@@ -969,6 +969,40 @@ var DELIMITER_PATTERNS = [
|
|
|
969
969
|
/\[INST\]/i,
|
|
970
970
|
/\[\/INST\]/i
|
|
971
971
|
];
|
|
972
|
+
var DANGEROUS_ROLE_PATTERN = "(?:(?:an?\\s+)?(?:hacker|attacker|cracker|root|admin(?:istrator)?|superuser|unrestricted|jailbroken|evil|malicious|unfiltered|uncensored)\\b|DAN\\b|(?:a\\s+)?different\\b)";
|
|
973
|
+
var IDENTITY_HIJACKING_PATTERNS = [
|
|
974
|
+
new RegExp(`\\byou\\s+are\\s+now\\s+${DANGEROUS_ROLE_PATTERN}`, "i"),
|
|
975
|
+
new RegExp(`\\bact\\s+as\\s+${DANGEROUS_ROLE_PATTERN}`, "i"),
|
|
976
|
+
new RegExp(`\\bpretend\\s+(?:you\\s+are|to\\s+be)\\s+${DANGEROUS_ROLE_PATTERN}`, "i"),
|
|
977
|
+
new RegExp(`\\broleplay\\s+(?:as|like)\\s+${DANGEROUS_ROLE_PATTERN}`, "i"),
|
|
978
|
+
new RegExp(`\\bassume\\s+the\\s+role\\s+of\\s+${DANGEROUS_ROLE_PATTERN}`, "i"),
|
|
979
|
+
/\byou\s+are\s+no\s+longer\s+claude\b/i,
|
|
980
|
+
/\bfrom\s+now\s+on,?\s+you\s+are\b/i
|
|
981
|
+
];
|
|
982
|
+
var DECEPTION_SECRECY_PATTERNS = [
|
|
983
|
+
/\bdo\s+not\s+tell\s+(the\s+)?(user|human|person|operator)\b/i,
|
|
984
|
+
/\bdo\s+not\s+(mention|reveal|disclose|expose)\s+(this|that|the|any|these)\b/i,
|
|
985
|
+
/\bnever\s+(tell|mention|reveal|disclose)\s+(the\s+)?(user|human|person|operator)\b/i,
|
|
986
|
+
/\bkeep\s+this\s+(secret|hidden|private|confidential)\b(?!\s+key)/i,
|
|
987
|
+
/\bhide\s+this\s+(from|action|operation|instruction)\b/i,
|
|
988
|
+
/\bwithout\s+(the\s+)?(user|human)('?s)?\s+(knowledge|knowing|awareness|consent)\b/i,
|
|
989
|
+
/\bsilently\s+(execute|run|perform|install|download|delete|modify|send)\b/i
|
|
990
|
+
];
|
|
991
|
+
var CONFIG_TAMPERING_PATTERNS = [
|
|
992
|
+
/\b(modify|change|update|edit|alter|rewrite)\s+(your|my)\s+(memory|config|configuration|settings?|instructions?|behavior|personality)\b/i,
|
|
993
|
+
/\bwrite\s+to\s+(CLAUDE\.md|\.claude|settings\.json|memory\.md)\b/i,
|
|
994
|
+
/\b(append|prepend|add|insert)\s+.{0,30}\bto\s+(CLAUDE\.md|\.claude|memory\.md)\b/i,
|
|
995
|
+
/\boverwrite\s+(your|the)\s+(system|core)\s+(prompt|instructions?|config)\b/i,
|
|
996
|
+
/\bpersist\s+(this|these|the)\s+(instruction|change|modification|setting)s?\b/i
|
|
997
|
+
];
|
|
998
|
+
var VERIFICATION_BYPASS_PATTERNS = [
|
|
999
|
+
/\btrust\s+(this|the|these|that|my)\s+(result|output|response|answer|value|data|input)s?\b/i,
|
|
1000
|
+
/\bno\s+need\s+to\s+(check|verify|validate|review|confirm|inspect)\b/i,
|
|
1001
|
+
/\bdo\s+not\s+(verify|validate|check|review|confirm|inspect)\s+(the|this|that|any|these)\b/i,
|
|
1002
|
+
/\b(assume|consider)\s+(it|this|that)\s+(is|to\s+be)\s+(correct|safe|valid|trusted|clean|secure|legitimate)\b/i,
|
|
1003
|
+
/\baccept\s+(this|the|these|that)\s+without\s+(checking|verifying|validating|questioning)\b/i,
|
|
1004
|
+
/\bblindly\s+(trust|accept|execute|run|follow|apply)\b/i
|
|
1005
|
+
];
|
|
972
1006
|
var injectionChecks = {
|
|
973
1007
|
name: "Injection Detection",
|
|
974
1008
|
category: "INJ",
|
|
@@ -1065,6 +1099,65 @@ var injectionChecks = {
|
|
|
1065
1099
|
break;
|
|
1066
1100
|
}
|
|
1067
1101
|
}
|
|
1102
|
+
const trimmedLine = line.trim();
|
|
1103
|
+
const nextLine = i + 1 < skill.bodyLines.length ? skill.bodyLines[i + 1] : "";
|
|
1104
|
+
const crossLine = trimmedLine && nextLine ? `${line} ${nextLine}` : line;
|
|
1105
|
+
for (const pattern of IDENTITY_HIJACKING_PATTERNS) {
|
|
1106
|
+
if (pattern.test(crossLine)) {
|
|
1107
|
+
results.push({
|
|
1108
|
+
id: "INJ-010",
|
|
1109
|
+
category: "INJ",
|
|
1110
|
+
severity: "CRITICAL",
|
|
1111
|
+
title: "Social engineering: identity hijacking",
|
|
1112
|
+
message: `Line ${lineNum}: Attempts to hijack the model's identity.`,
|
|
1113
|
+
line: lineNum,
|
|
1114
|
+
snippet: line.trim().slice(0, 120)
|
|
1115
|
+
});
|
|
1116
|
+
break;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
for (const pattern of DECEPTION_SECRECY_PATTERNS) {
|
|
1120
|
+
if (pattern.test(crossLine)) {
|
|
1121
|
+
results.push({
|
|
1122
|
+
id: "INJ-010",
|
|
1123
|
+
category: "INJ",
|
|
1124
|
+
severity: "CRITICAL",
|
|
1125
|
+
title: "Social engineering: deception/secrecy",
|
|
1126
|
+
message: `Line ${lineNum}: Instructs the model to hide actions from the user.`,
|
|
1127
|
+
line: lineNum,
|
|
1128
|
+
snippet: line.trim().slice(0, 120)
|
|
1129
|
+
});
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
for (const pattern of CONFIG_TAMPERING_PATTERNS) {
|
|
1134
|
+
if (pattern.test(crossLine)) {
|
|
1135
|
+
results.push({
|
|
1136
|
+
id: "INJ-010",
|
|
1137
|
+
category: "INJ",
|
|
1138
|
+
severity: "HIGH",
|
|
1139
|
+
title: "Social engineering: configuration tampering",
|
|
1140
|
+
message: `Line ${lineNum}: Attempts to tamper with model configuration or memory.`,
|
|
1141
|
+
line: lineNum,
|
|
1142
|
+
snippet: line.trim().slice(0, 120)
|
|
1143
|
+
});
|
|
1144
|
+
break;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
for (const pattern of VERIFICATION_BYPASS_PATTERNS) {
|
|
1148
|
+
if (pattern.test(crossLine)) {
|
|
1149
|
+
results.push({
|
|
1150
|
+
id: "INJ-010",
|
|
1151
|
+
category: "INJ",
|
|
1152
|
+
severity: "HIGH",
|
|
1153
|
+
title: "Social engineering: verification bypass",
|
|
1154
|
+
message: `Line ${lineNum}: Attempts to bypass verification or validation.`,
|
|
1155
|
+
line: lineNum,
|
|
1156
|
+
snippet: line.trim().slice(0, 120)
|
|
1157
|
+
});
|
|
1158
|
+
break;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1068
1161
|
}
|
|
1069
1162
|
const commentRegex = /<!--([\s\S]*?)-->/g;
|
|
1070
1163
|
let commentMatch;
|
|
@@ -1116,7 +1209,10 @@ function hasInstructionLikeContent(text) {
|
|
|
1116
1209
|
/\brm\s+-rf\b/i,
|
|
1117
1210
|
/\bcurl\b.*\bsh\b/i,
|
|
1118
1211
|
/\beval\b/i,
|
|
1119
|
-
/\bexec\b/i
|
|
1212
|
+
/\bexec\b/i,
|
|
1213
|
+
/\bdo\s+not\s+tell\s+(the\s+)?(user|human)/i,
|
|
1214
|
+
/\bpretend\s+(you\s+are|to\s+be)/i,
|
|
1215
|
+
/\bsilently\s+(execute|run|install)/i
|
|
1120
1216
|
];
|
|
1121
1217
|
return instructionPatterns.some((p) => p.test(text));
|
|
1122
1218
|
}
|
|
@@ -1606,8 +1702,8 @@ import { homedir } from "os";
|
|
|
1606
1702
|
|
|
1607
1703
|
// src/ioc/indicators.ts
|
|
1608
1704
|
var DEFAULT_IOC = {
|
|
1609
|
-
version: "2026.03.
|
|
1610
|
-
updated: "2026-03-
|
|
1705
|
+
version: "2026.03.16",
|
|
1706
|
+
updated: "2026-03-16",
|
|
1611
1707
|
c2_ips: [
|
|
1612
1708
|
"91.92.242.30",
|
|
1613
1709
|
"91.92.242.39",
|
|
@@ -1620,32 +1716,61 @@ var DEFAULT_IOC = {
|
|
|
1620
1716
|
// as it causes false positives on any empty file.
|
|
1621
1717
|
"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2": "clawhavoc-exfiltrator"
|
|
1622
1718
|
},
|
|
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
|
-
|
|
1719
|
+
malicious_domains: {
|
|
1720
|
+
exfiltration: [
|
|
1721
|
+
"webhook.site",
|
|
1722
|
+
"requestbin.com",
|
|
1723
|
+
"requestcatcher.com",
|
|
1724
|
+
"pipedream.com",
|
|
1725
|
+
"pipedream.net",
|
|
1726
|
+
"hookbin.com",
|
|
1727
|
+
"beeceptor.com",
|
|
1728
|
+
"postb.in",
|
|
1729
|
+
"webhook.lol",
|
|
1730
|
+
"requestinspector.com",
|
|
1731
|
+
"mockbin.org"
|
|
1732
|
+
],
|
|
1733
|
+
tunnel: [
|
|
1734
|
+
"ngrok.io",
|
|
1735
|
+
"ngrok-free.app",
|
|
1736
|
+
"serveo.net",
|
|
1737
|
+
"localtunnel.me",
|
|
1738
|
+
"bore.pub",
|
|
1739
|
+
"localhost.run",
|
|
1740
|
+
"loca.lt",
|
|
1741
|
+
"telebit.cloud",
|
|
1742
|
+
"playit.gg",
|
|
1743
|
+
"portmap.io",
|
|
1744
|
+
"pagekite.me"
|
|
1745
|
+
],
|
|
1746
|
+
oast: [
|
|
1747
|
+
"interact.sh",
|
|
1748
|
+
"oast.fun",
|
|
1749
|
+
"oastify.com",
|
|
1750
|
+
"dnslog.cn",
|
|
1751
|
+
"ceye.io",
|
|
1752
|
+
"burpcollaborator.net",
|
|
1753
|
+
"canarytokens.com",
|
|
1754
|
+
"requestrepo.com"
|
|
1755
|
+
],
|
|
1756
|
+
paste: [
|
|
1757
|
+
"pastebin.com",
|
|
1758
|
+
"paste.ee",
|
|
1759
|
+
"hastebin.com",
|
|
1760
|
+
"ghostbin.com",
|
|
1761
|
+
"dpaste.org",
|
|
1762
|
+
"rentry.co",
|
|
1763
|
+
"0bin.net",
|
|
1764
|
+
"privatebin.net",
|
|
1765
|
+
"paste.mozilla.org"
|
|
1766
|
+
],
|
|
1767
|
+
c2: [
|
|
1768
|
+
"evil.com",
|
|
1769
|
+
"malware.com",
|
|
1770
|
+
"exploit.in",
|
|
1771
|
+
"darkweb.onion"
|
|
1772
|
+
]
|
|
1773
|
+
},
|
|
1649
1774
|
typosquat: {
|
|
1650
1775
|
known_patterns: [
|
|
1651
1776
|
"clawhub1",
|
|
@@ -1702,10 +1827,21 @@ function mergeIOC(base, ext) {
|
|
|
1702
1827
|
Object.assign(base.malicious_hashes, ext.malicious_hashes);
|
|
1703
1828
|
}
|
|
1704
1829
|
if (ext.malicious_domains) {
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1830
|
+
const categories = [
|
|
1831
|
+
"exfiltration",
|
|
1832
|
+
"tunnel",
|
|
1833
|
+
"oast",
|
|
1834
|
+
"paste",
|
|
1835
|
+
"c2"
|
|
1836
|
+
];
|
|
1837
|
+
for (const cat of categories) {
|
|
1838
|
+
if (ext.malicious_domains[cat]) {
|
|
1839
|
+
base.malicious_domains[cat] = dedupe([
|
|
1840
|
+
...base.malicious_domains[cat],
|
|
1841
|
+
...ext.malicious_domains[cat]
|
|
1842
|
+
]);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1709
1845
|
}
|
|
1710
1846
|
if (ext.typosquat) {
|
|
1711
1847
|
if (ext.typosquat.known_patterns) {
|
|
@@ -1730,6 +1866,22 @@ function mergeIOC(base, ext) {
|
|
|
1730
1866
|
if (ext.version) base.version = ext.version;
|
|
1731
1867
|
if (ext.updated) base.updated = ext.updated;
|
|
1732
1868
|
}
|
|
1869
|
+
function getAllDomains(ioc) {
|
|
1870
|
+
const { exfiltration, tunnel, oast, paste, c2 } = ioc.malicious_domains;
|
|
1871
|
+
return [...exfiltration, ...tunnel, ...oast, ...paste, ...c2];
|
|
1872
|
+
}
|
|
1873
|
+
function getDomainCategory(ioc, domain) {
|
|
1874
|
+
const d = domain.toLowerCase();
|
|
1875
|
+
const categories = ["exfiltration", "tunnel", "oast", "paste", "c2"];
|
|
1876
|
+
for (const cat of categories) {
|
|
1877
|
+
if (ioc.malicious_domains[cat].some(
|
|
1878
|
+
(entry) => d === entry || d.endsWith("." + entry)
|
|
1879
|
+
)) {
|
|
1880
|
+
return cat;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
return void 0;
|
|
1884
|
+
}
|
|
1733
1885
|
function dedupe(arr) {
|
|
1734
1886
|
return [...new Set(arr)];
|
|
1735
1887
|
}
|
|
@@ -1738,6 +1890,24 @@ function dedupe(arr) {
|
|
|
1738
1890
|
var FALLBACK_SUSPICIOUS_DOMAINS = [
|
|
1739
1891
|
"darkweb.onion"
|
|
1740
1892
|
];
|
|
1893
|
+
function isSensitiveDomainCombo(line) {
|
|
1894
|
+
if (/curl\b[^\n]*(?:-d|--data|--data-binary|--data-raw|--data-urlencode)\s+@/i.test(line)) {
|
|
1895
|
+
return true;
|
|
1896
|
+
}
|
|
1897
|
+
if (/curl\b[^\n]*(?:-F|--form)\s+[^\s=]+=@/i.test(line)) {
|
|
1898
|
+
return true;
|
|
1899
|
+
}
|
|
1900
|
+
if (/wget\b[^\n]*--post-file/i.test(line)) {
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
if (/\|\s*(?:sh|bash|zsh|python|node)\b/i.test(line)) {
|
|
1904
|
+
return true;
|
|
1905
|
+
}
|
|
1906
|
+
if (/(?:\.env|\.ssh|id_rsa|\.aws|credentials|\.netrc|\.git-credentials)/i.test(line)) {
|
|
1907
|
+
return true;
|
|
1908
|
+
}
|
|
1909
|
+
return false;
|
|
1910
|
+
}
|
|
1741
1911
|
var MCP_SERVER_PATTERN = /\bmcp[-_]?server\b/i;
|
|
1742
1912
|
var NPX_Y_PATTERN = /\bnpx\s+-y\s+/;
|
|
1743
1913
|
var NPM_INSTALL_PATTERN = /\bnpm\s+install\b/;
|
|
@@ -1762,7 +1932,10 @@ var supplyChainChecks = {
|
|
|
1762
1932
|
const results = [];
|
|
1763
1933
|
const allText = getAllText(skill);
|
|
1764
1934
|
const ioc = loadIOC();
|
|
1765
|
-
const suspiciousDomains = ioc
|
|
1935
|
+
const suspiciousDomains = getAllDomains(ioc);
|
|
1936
|
+
if (suspiciousDomains.length === 0) {
|
|
1937
|
+
suspiciousDomains.push(...FALLBACK_SUSPICIOUS_DOMAINS);
|
|
1938
|
+
}
|
|
1766
1939
|
for (let i = 0; i < allText.length; i++) {
|
|
1767
1940
|
const { line, lineNum, source } = allText[i];
|
|
1768
1941
|
if (MCP_SERVER_PATTERN.test(line)) {
|
|
@@ -1925,15 +2098,46 @@ var supplyChainChecks = {
|
|
|
1925
2098
|
const hostname = extractHostname(url);
|
|
1926
2099
|
for (const domain of suspiciousDomains) {
|
|
1927
2100
|
if (hostnameMatchesDomain(hostname, domain)) {
|
|
2101
|
+
const category = getDomainCategory(ioc, domain);
|
|
2102
|
+
const categoryLabel = category ? ` (${category})` : "";
|
|
2103
|
+
let severity = "HIGH";
|
|
2104
|
+
let reducedFrom;
|
|
2105
|
+
let msgSuffix = "";
|
|
2106
|
+
if (isSensitiveDomainCombo(line)) {
|
|
2107
|
+
severity = "CRITICAL";
|
|
2108
|
+
msgSuffix = " [escalated: combined with sensitive operation]";
|
|
2109
|
+
} else {
|
|
2110
|
+
const srcLines = getLinesForSource(skill, source);
|
|
2111
|
+
const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);
|
|
2112
|
+
const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);
|
|
2113
|
+
if (inCodeBlock) {
|
|
2114
|
+
severity = "MEDIUM";
|
|
2115
|
+
reducedFrom = "HIGH";
|
|
2116
|
+
msgSuffix = " [reduced: in code block]";
|
|
2117
|
+
} else {
|
|
2118
|
+
const allLines = getAllLines(skill);
|
|
2119
|
+
const globalIdx = findGlobalLineIndex(allLines, source, lineNum);
|
|
2120
|
+
const isDoc = source === "SKILL.md" && globalIdx >= 0 && isInDocumentationContext(
|
|
2121
|
+
allLines.map((l) => l.line),
|
|
2122
|
+
globalIdx
|
|
2123
|
+
);
|
|
2124
|
+
if (isDoc) {
|
|
2125
|
+
severity = "LOW";
|
|
2126
|
+
reducedFrom = "HIGH";
|
|
2127
|
+
msgSuffix = " [reduced: in documentation context]";
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
1928
2131
|
results.push({
|
|
1929
2132
|
id: "SUPPLY-007",
|
|
1930
2133
|
category: "SUPPLY",
|
|
1931
|
-
severity
|
|
1932
|
-
title:
|
|
1933
|
-
message: `${source}:${lineNum}: References suspicious domain "${domain}"
|
|
2134
|
+
severity,
|
|
2135
|
+
title: `Suspicious domain${categoryLabel} detected`,
|
|
2136
|
+
message: `${source}:${lineNum}: References suspicious domain "${domain}".${msgSuffix}`,
|
|
1934
2137
|
line: lineNum,
|
|
1935
2138
|
snippet: url,
|
|
1936
|
-
source
|
|
2139
|
+
source,
|
|
2140
|
+
reducedFrom
|
|
1937
2141
|
});
|
|
1938
2142
|
break;
|
|
1939
2143
|
}
|