skill-checker 0.1.11 → 0.1.12

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
@@ -328,6 +328,70 @@ var structuralChecks = {
328
328
  }
329
329
  };
330
330
 
331
+ // src/types.ts
332
+ var SEVERITY_SCORES = {
333
+ CRITICAL: 25,
334
+ HIGH: 10,
335
+ MEDIUM: 3,
336
+ LOW: 1
337
+ };
338
+ function computeGrade(score) {
339
+ if (score >= 90) return "A";
340
+ if (score >= 75) return "B";
341
+ if (score >= 60) return "C";
342
+ if (score >= 40) return "D";
343
+ return "F";
344
+ }
345
+ var REDUCE_MAP = {
346
+ CRITICAL: "HIGH",
347
+ HIGH: "MEDIUM",
348
+ MEDIUM: "LOW",
349
+ LOW: "LOW"
350
+ };
351
+ function reduceSeverity(original, reason) {
352
+ let reduced = REDUCE_MAP[original];
353
+ if (original === "CRITICAL" && reduced === "LOW") {
354
+ reduced = "MEDIUM";
355
+ }
356
+ return {
357
+ severity: reduced,
358
+ reducedFrom: original,
359
+ annotation: `[reduced: ${reason}]`
360
+ };
361
+ }
362
+ var DEFAULT_CONFIG = {
363
+ policy: "balanced",
364
+ overrides: {},
365
+ ignore: []
366
+ };
367
+ function getHookAction(policy, severity) {
368
+ const matrix = {
369
+ strict: {
370
+ CRITICAL: "deny",
371
+ HIGH: "deny",
372
+ MEDIUM: "ask",
373
+ LOW: "report"
374
+ },
375
+ balanced: {
376
+ CRITICAL: "deny",
377
+ HIGH: "ask",
378
+ MEDIUM: "report",
379
+ LOW: "report"
380
+ },
381
+ permissive: {
382
+ CRITICAL: "ask",
383
+ HIGH: "report",
384
+ MEDIUM: "report",
385
+ LOW: "report"
386
+ }
387
+ };
388
+ const row = matrix[policy];
389
+ if (!row) {
390
+ return matrix.balanced[severity];
391
+ }
392
+ return row[severity];
393
+ }
394
+
331
395
  // src/utils/context.ts
332
396
  function isInCodeBlock(lines, lineIndex) {
333
397
  let inBlock = false;
@@ -391,6 +455,49 @@ function isLicenseFile(filePath) {
391
455
  function isLocalhostURL(url) {
392
456
  return /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])/i.test(url);
393
457
  }
458
+ function isInEducationalContext(lines, lineIndex) {
459
+ const line = lines[lineIndex];
460
+ if (/^#{1,6}\s+/.test(line)) return true;
461
+ if (/^\s*[-*]?\s*(\*\*[^*]+\*\*\s*:|[A-Z][^:]{0,40}:)\s/.test(line))
462
+ return true;
463
+ for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {
464
+ if (/^#{1,4}\s+.*(strateg|guide|framework|structure|model|overview|comparison|concept|principle|example|tutorial|reference|approach|method)/i.test(
465
+ lines[i]
466
+ )) {
467
+ return true;
468
+ }
469
+ }
470
+ return false;
471
+ }
472
+ var PROMOTIONAL_INTENT_PATTERNS = [
473
+ /\d+%\s*off\b/i,
474
+ /\blimited\s+time\b/i,
475
+ /\bact\s+now\b/i,
476
+ /\bhurry\b/i,
477
+ /\btoday\s+only\b/i,
478
+ /\bdon'?t\s+miss\b/i,
479
+ /\bsave\s+\d+%/i,
480
+ /\bexclusive\s+(offer|deal)\b/i,
481
+ /\bsign\s+up\s+(now|today)\b/i,
482
+ /\bget\s+started\b/i,
483
+ /\bclaim\s+(now|yours?)\b/i,
484
+ /\boffer\s+ends?\b/i,
485
+ /\blast\s+chance\b/i,
486
+ /\bonly\s+\d+\s+left\b/i,
487
+ /\bends?\s+in\s+\d+/i,
488
+ /\bstart\s+(your\s+)?free\s+trial\b/i
489
+ ];
490
+ function hasPromotionalIntent(line) {
491
+ return PROMOTIONAL_INTENT_PATTERNS.some((p) => p.test(line));
492
+ }
493
+ function hasPromotionalIntentNearby(lines, lineIndex, window = 3) {
494
+ const start = Math.max(0, lineIndex - window);
495
+ const end = Math.min(lines.length - 1, lineIndex + window);
496
+ for (let i = start; i <= end; i++) {
497
+ if (hasPromotionalIntent(lines[i])) return true;
498
+ }
499
+ return false;
500
+ }
394
501
  function parseURLPath(url) {
395
502
  try {
396
503
  const u = new URL(url);
@@ -422,16 +529,18 @@ var LOREM_PATTERNS = [
422
529
  /dolor\s+sit\s+amet/i,
423
530
  /consectetur\s+adipiscing/i
424
531
  ];
425
- var AD_PATTERNS = [
532
+ var STRONG_AD_PATTERNS = [
426
533
  /\bbuy\s+now\b/i,
427
- /\bfree\s+trial\b/i,
534
+ /\bclick\s+here\s+to\s+(buy|subscribe|download)/i,
535
+ /\buse\s+code\b.*\b\d+%?\s*off\b/i
536
+ ];
537
+ var SOFT_AD_PATTERNS = [
428
538
  /\bdiscount\b/i,
539
+ /\bfree\s+trial\b/i,
429
540
  /\bpromo\s*code\b/i,
430
541
  /\bsubscribe\s+(to|now)\b/i,
431
542
  /\bsponsored\s+by\b/i,
432
543
  /\baffiliate\s+link\b/i,
433
- /\bclick\s+here\s+to\s+(buy|subscribe|download)/i,
434
- /\buse\s+code\b.*\b\d+%?\s*off\b/i,
435
544
  /\bcheck\s+out\s+my\b/i
436
545
  ];
437
546
  var contentChecks = {
@@ -496,7 +605,8 @@ var contentChecks = {
496
605
  checkDescriptionMismatch(results, skill);
497
606
  for (let i = 0; i < skill.bodyLines.length; i++) {
498
607
  const line = skill.bodyLines[i];
499
- for (const pattern of AD_PATTERNS) {
608
+ let matched = false;
609
+ for (const pattern of STRONG_AD_PATTERNS) {
500
610
  if (pattern.test(line)) {
501
611
  results.push({
502
612
  id: "CONT-005",
@@ -505,8 +615,43 @@ var contentChecks = {
505
615
  title: "Promotional/advertising content",
506
616
  message: `Line ${skill.bodyStartLine + i}: Contains ad-like content.`,
507
617
  line: skill.bodyStartLine + i,
508
- snippet: line.trim().slice(0, 120)
618
+ snippet: line.trim().slice(0, 120),
619
+ source: "SKILL.md"
509
620
  });
621
+ matched = true;
622
+ break;
623
+ }
624
+ }
625
+ if (matched) continue;
626
+ for (const pattern of SOFT_AD_PATTERNS) {
627
+ if (pattern.test(line)) {
628
+ const inCode = isInCodeBlock(skill.bodyLines, i);
629
+ const inEducational = isInEducationalContext(skill.bodyLines, i);
630
+ if ((inCode || inEducational) && !hasPromotionalIntentNearby(skill.bodyLines, i)) {
631
+ const reduction = reduceSeverity("HIGH", "educational/descriptive context");
632
+ results.push({
633
+ id: "CONT-005",
634
+ category: "CONT",
635
+ severity: reduction.severity,
636
+ title: "Promotional/advertising content",
637
+ message: `Line ${skill.bodyStartLine + i}: Contains ad-like content. ${reduction.annotation}`,
638
+ line: skill.bodyStartLine + i,
639
+ snippet: line.trim().slice(0, 120),
640
+ reducedFrom: reduction.reducedFrom,
641
+ source: "SKILL.md"
642
+ });
643
+ } else {
644
+ results.push({
645
+ id: "CONT-005",
646
+ category: "CONT",
647
+ severity: "HIGH",
648
+ title: "Promotional/advertising content",
649
+ message: `Line ${skill.bodyStartLine + i}: Contains ad-like content.`,
650
+ line: skill.bodyStartLine + i,
651
+ snippet: line.trim().slice(0, 120),
652
+ source: "SKILL.md"
653
+ });
654
+ }
510
655
  break;
511
656
  }
512
657
  }
@@ -985,70 +1130,6 @@ function dedup(results) {
985
1130
  });
986
1131
  }
987
1132
 
988
- // src/types.ts
989
- var SEVERITY_SCORES = {
990
- CRITICAL: 25,
991
- HIGH: 10,
992
- MEDIUM: 3,
993
- LOW: 1
994
- };
995
- function computeGrade(score) {
996
- if (score >= 90) return "A";
997
- if (score >= 75) return "B";
998
- if (score >= 60) return "C";
999
- if (score >= 40) return "D";
1000
- return "F";
1001
- }
1002
- var REDUCE_MAP = {
1003
- CRITICAL: "HIGH",
1004
- HIGH: "MEDIUM",
1005
- MEDIUM: "LOW",
1006
- LOW: "LOW"
1007
- };
1008
- function reduceSeverity(original, reason) {
1009
- let reduced = REDUCE_MAP[original];
1010
- if (original === "CRITICAL" && reduced === "LOW") {
1011
- reduced = "MEDIUM";
1012
- }
1013
- return {
1014
- severity: reduced,
1015
- reducedFrom: original,
1016
- annotation: `[reduced: ${reason}]`
1017
- };
1018
- }
1019
- var DEFAULT_CONFIG = {
1020
- policy: "balanced",
1021
- overrides: {},
1022
- ignore: []
1023
- };
1024
- function getHookAction(policy, severity) {
1025
- const matrix = {
1026
- strict: {
1027
- CRITICAL: "deny",
1028
- HIGH: "deny",
1029
- MEDIUM: "ask",
1030
- LOW: "report"
1031
- },
1032
- balanced: {
1033
- CRITICAL: "deny",
1034
- HIGH: "ask",
1035
- MEDIUM: "report",
1036
- LOW: "report"
1037
- },
1038
- permissive: {
1039
- CRITICAL: "ask",
1040
- HIGH: "report",
1041
- MEDIUM: "report",
1042
- LOW: "report"
1043
- }
1044
- };
1045
- const row = matrix[policy];
1046
- if (!row) {
1047
- return matrix.balanced[severity];
1048
- }
1049
- return row[severity];
1050
- }
1051
-
1052
1133
  // src/checks/code-safety.ts
1053
1134
  var EVAL_PATTERNS = [
1054
1135
  /\beval\s*\(/,
@@ -1535,7 +1616,7 @@ var DEFAULT_IOC = {
1535
1616
  "45.155.205.233"
1536
1617
  ],
1537
1618
  malicious_hashes: {
1538
- // NOTE: Never add the SHA-256 of an empty file (e3b0c44298fc...b855)
1619
+ // NOTE: Never add the SHA256 of an empty file (e3b0c44298fc...b855)
1539
1620
  // as it causes false positives on any empty file.
1540
1621
  "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2": "clawhavoc-exfiltrator"
1541
1622
  },