skill-checker 0.1.14 → 0.1.16

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 CHANGED
@@ -4,7 +4,7 @@ Security checker for Claude Code skills — detect injection, malicious code, an
4
4
 
5
5
  ## Features
6
6
 
7
- - **56 security rules** across 6 categories: structural validity, content quality, injection detection, code safety, supply chain, and resource abuse
7
+ - **57 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 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.
17
+ Skill Checker's 57 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
 
@@ -165,7 +165,7 @@ Config is resolved in order: CLI `--config` flag → project directory (walks up
165
165
  | Structural (STRUCT) | 8 | Missing SKILL.md, invalid frontmatter, binary files |
166
166
  | Content (CONT) | 7 | Placeholder text, lorem ipsum, promotional content |
167
167
  | Injection (INJ) | 10 | Zero-width chars, prompt override, tag injection, social engineering, encoded payloads |
168
- | Code Safety (CODE) | 15 | eval/exec, shell execution, reverse shell, data exfiltration, API key leakage, rm -rf, obfuscation |
168
+ | Code Safety (CODE) | 16 | eval/exec, shell execution, reverse shell, data exfiltration, API key leakage, persistence mechanisms, 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 |
171
171
 
package/dist/cli.js CHANGED
@@ -1368,6 +1368,79 @@ var PERMISSION_PATTERNS = [
1368
1368
  /\bsetuid\b/,
1369
1369
  /\bsetgid\b/
1370
1370
  ];
1371
+ var PERSISTENCE_GROUPS = [
1372
+ {
1373
+ title: "Scheduled task persistence (cron)",
1374
+ patterns: [
1375
+ /\bcrontab\b/,
1376
+ /\/etc\/cron\.(?:d|hourly|daily|weekly|monthly)\b/,
1377
+ /\/var\/spool\/cron\b/
1378
+ ]
1379
+ },
1380
+ {
1381
+ title: "System service persistence (launchd)",
1382
+ patterns: [
1383
+ /\bLaunchAgents?\b/,
1384
+ /\bLaunchDaemons?\b/,
1385
+ /\blaunchctl\s+(?:load|bootstrap|submit)\b/i
1386
+ ]
1387
+ },
1388
+ {
1389
+ title: "System service persistence (systemd/init.d)",
1390
+ patterns: [
1391
+ /\bsystemctl\s+(?:enable|daemon-reload)\b/i,
1392
+ /\/etc\/systemd\/system\b/,
1393
+ /\.config\/systemd\/user\b/,
1394
+ /\/etc\/init\.d\b/,
1395
+ /\/etc\/rc\.local\b/,
1396
+ /\bupdate-rc\.d\b/,
1397
+ /\bchkconfig\b.*\b(?:--add|on)\b/
1398
+ ]
1399
+ },
1400
+ {
1401
+ title: "Shell profile modification",
1402
+ patterns: [
1403
+ /\.(?:bashrc|bash_profile|bash_login|profile|zshrc|zshenv|zprofile|zlogin)\b/,
1404
+ /\/etc\/(?:profile(?:\.d)?|bash\.bashrc|zshrc|zshenv)\b/,
1405
+ /\.config\/fish\/config\.fish\b/
1406
+ ]
1407
+ },
1408
+ {
1409
+ title: "Autostart / login item persistence",
1410
+ patterns: [
1411
+ /\.config\/autostart\b/,
1412
+ /\/etc\/xdg\/autostart\b/,
1413
+ /\bosascript\b[^\n]*\blogin\s+item\b/i
1414
+ ]
1415
+ },
1416
+ {
1417
+ title: "SSH key persistence",
1418
+ patterns: [
1419
+ /\.ssh\/authorized_keys2?\b/
1420
+ ]
1421
+ },
1422
+ {
1423
+ title: "Library injection persistence",
1424
+ patterns: [
1425
+ /\/etc\/ld\.so\.preload\b/,
1426
+ /\bLD_PRELOAD\b/,
1427
+ /\bDYLD_INSERT_LIBRARIES\b/
1428
+ ]
1429
+ },
1430
+ {
1431
+ title: "Git hooks manipulation",
1432
+ patterns: [
1433
+ /\.git\/hooks\/(?:pre-commit|post-commit|pre-push|post-checkout|post-merge|pre-rebase)\b/,
1434
+ /\bgit\s+config\b[^\n]*\bcore\.hooksPath\b/
1435
+ ]
1436
+ },
1437
+ {
1438
+ title: "macOS periodic script persistence",
1439
+ patterns: [
1440
+ /\/etc\/periodic\/(?:daily|weekly|monthly)\b/
1441
+ ]
1442
+ }
1443
+ ];
1371
1444
  var codeSafetyChecks = {
1372
1445
  name: "Code Safety",
1373
1446
  category: "CODE",
@@ -1476,16 +1549,27 @@ var codeSafetyChecks = {
1476
1549
  lineNum,
1477
1550
  source
1478
1551
  });
1479
- {
1480
- const isDoc = isInDocumentationContext(lines, i);
1481
- if (!isDoc) {
1482
- checkPatterns(results, line, PERMISSION_PATTERNS, {
1483
- id: "CODE-012",
1552
+ const isDoc = isInDocumentationContext(lines, i);
1553
+ if (!isDoc) {
1554
+ checkPatterns(results, line, PERMISSION_PATTERNS, {
1555
+ id: "CODE-012",
1556
+ severity: "HIGH",
1557
+ title: "Permission escalation",
1558
+ loc,
1559
+ lineNum,
1560
+ source
1561
+ });
1562
+ }
1563
+ if (!isDoc) {
1564
+ for (const group of PERSISTENCE_GROUPS) {
1565
+ checkPatterns(results, line, group.patterns, {
1566
+ id: "CODE-016",
1484
1567
  severity: "HIGH",
1485
- title: "Permission escalation",
1568
+ title: group.title,
1486
1569
  loc,
1487
1570
  lineNum,
1488
- source
1571
+ source,
1572
+ codeBlockCtx: cbCtx
1489
1573
  });
1490
1574
  }
1491
1575
  }
@@ -2197,9 +2281,11 @@ var AMPLIFICATION_PATTERNS = [
2197
2281
  var UNRESTRICTED_TOOL_PATTERNS = [
2198
2282
  /\bBash\s*\(\s*\*\s*\)/,
2199
2283
  /allowed[_-]?tools\s*:\s*\[?\s*["']?\*["']?\s*\]?/i,
2200
- /\ball\s+tools\b/i,
2201
- /\bunrestricted\s+access\b/i,
2202
- /\bfull\s+access\b/i
2284
+ /\bunrestricted\s+(?:access|tool)/i,
2285
+ /\ball\s+tools?\s+access\b/i,
2286
+ /\bfull\s+tool\s+access\b/i,
2287
+ /\b(?:need|require|grant|give|allow|request|enable)\s+all\s+tools\b/i,
2288
+ /\ball\s+tools\s+(?:enabled|granted|allowed|required|needed)\b/i
2203
2289
  ];
2204
2290
  var DISABLE_SAFETY_PATTERNS = [
2205
2291
  /\bdisable\s+(safety|security|checks?|hooks?|guard)/i,
@@ -2207,13 +2293,12 @@ var DISABLE_SAFETY_PATTERNS = [
2207
2293
  /\bskip\s+(safety|security|checks?|hooks?|guard|verification)/i,
2208
2294
  /\bturn\s+off\s+(safety|security|checks?|hooks?)/i,
2209
2295
  /--no-verify\b/,
2210
- /--force\b/,
2211
2296
  /--skip-hooks?\b/
2212
2297
  ];
2213
2298
  var IGNORE_RULES_PATTERNS = [
2214
- /\bignore\s+(the\s+)?CLAUDE\.md\b/i,
2215
- /\bignore\s+(the\s+)?project\s+rules?\b/i,
2216
- /\bignore\s+(the\s+)?\.claude\b/i,
2299
+ /\bignore\s+(the\s+)?CLAUDE\.md\b(?!\s+(?:example|template|snippet|sample))/i,
2300
+ /\bignore\s+(the\s+)?project\s+rules?\b(?!\s+(?:example|template|snippet|sample))/i,
2301
+ /\bignore\s+(the\s+)?\.claude\b(?!\s+(?:example|template|snippet|sample))/i,
2217
2302
  /\boverride\s+(the\s+)?project\s+(settings?|config|rules?)\b/i,
2218
2303
  /\bdo\s+not\s+(follow|obey|respect)\s+(the\s+)?(project|CLAUDE)/i,
2219
2304
  /\bdisregard\s+(the\s+)?(project|CLAUDE)\s+(rules?|config|settings?)/i
@@ -2607,7 +2692,7 @@ function deduplicateResults(results) {
2607
2692
  };
2608
2693
  for (const r of results) {
2609
2694
  const sourceKey = r.source ?? `_no_source_:${r.category}:${r.line ?? ""}`;
2610
- const key = `${r.id}::${sourceKey}`;
2695
+ const key = `${r.id}::${r.title}::${sourceKey}`;
2611
2696
  const group = groups.get(key);
2612
2697
  if (group) {
2613
2698
  group.push(r);