skill-checker 0.1.7 → 0.1.8
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 +12 -22
- package/dist/cli.js +82 -24
- package/dist/cli.js.map +1 -1
- package/dist/index.js +75 -24
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -206,15 +206,12 @@ ${tailContent}` : headContent;
|
|
|
206
206
|
// src/checks/structural.ts
|
|
207
207
|
var HYPHEN_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
208
208
|
var MAX_NAME_LENGTH = 64;
|
|
209
|
-
var
|
|
210
|
-
".exe",
|
|
211
|
-
".bat",
|
|
212
|
-
".cmd",
|
|
209
|
+
var SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
213
210
|
".sh",
|
|
214
211
|
".bash",
|
|
215
212
|
".ps1",
|
|
216
|
-
".
|
|
217
|
-
".
|
|
213
|
+
".bat",
|
|
214
|
+
".cmd"
|
|
218
215
|
]);
|
|
219
216
|
var BINARY_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
220
217
|
".exe",
|
|
@@ -226,6 +223,10 @@ var BINARY_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
|
226
223
|
".class",
|
|
227
224
|
".pyc"
|
|
228
225
|
]);
|
|
226
|
+
var INSTALLER_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
227
|
+
".com",
|
|
228
|
+
".msi"
|
|
229
|
+
]);
|
|
229
230
|
var structuralChecks = {
|
|
230
231
|
name: "Structural Validity",
|
|
231
232
|
category: "STRUCT",
|
|
@@ -279,7 +280,7 @@ var structuralChecks = {
|
|
|
279
280
|
}
|
|
280
281
|
for (const file of skill.files) {
|
|
281
282
|
const ext = file.extension.toLowerCase();
|
|
282
|
-
if (BINARY_EXTENSIONS2.has(ext) ||
|
|
283
|
+
if (BINARY_EXTENSIONS2.has(ext) || INSTALLER_EXTENSIONS.has(ext)) {
|
|
283
284
|
results.push({
|
|
284
285
|
id: "STRUCT-006",
|
|
285
286
|
category: "STRUCT",
|
|
@@ -288,6 +289,15 @@ var structuralChecks = {
|
|
|
288
289
|
message: `Found unexpected file: ${file.path} (${ext})`,
|
|
289
290
|
source: file.path
|
|
290
291
|
});
|
|
292
|
+
} else if (SCRIPT_EXTENSIONS.has(ext)) {
|
|
293
|
+
results.push({
|
|
294
|
+
id: "STRUCT-006",
|
|
295
|
+
category: "STRUCT",
|
|
296
|
+
severity: "LOW",
|
|
297
|
+
title: "Script file present",
|
|
298
|
+
message: `Found script file: ${file.path} (${ext}). Content is scanned separately.`,
|
|
299
|
+
source: file.path
|
|
300
|
+
});
|
|
291
301
|
}
|
|
292
302
|
}
|
|
293
303
|
const name = skill.frontmatter.name;
|
|
@@ -372,6 +382,15 @@ function isInDocumentationContext(lines, lineIndex) {
|
|
|
372
382
|
}
|
|
373
383
|
return false;
|
|
374
384
|
}
|
|
385
|
+
function isNearDocumentationHeader(lines, lineIndex) {
|
|
386
|
+
for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {
|
|
387
|
+
const l = lines[i];
|
|
388
|
+
if (/^#{1,4}\s+.*(install|setup|prerequisite|requirement|depend|getting\s+started|quickstart)/i.test(l)) {
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
375
394
|
function isLicenseFile(filePath) {
|
|
376
395
|
const name = filePath.split("/").pop()?.toUpperCase() ?? "";
|
|
377
396
|
const base = name.replace(/\.[^.]+$/, "");
|
|
@@ -1715,14 +1734,58 @@ var supplyChainChecks = {
|
|
|
1715
1734
|
if (NPM_INSTALL_PATTERN.test(line) || PIP_INSTALL_PATTERN.test(line)) {
|
|
1716
1735
|
const allLines = getAllLines(skill);
|
|
1717
1736
|
const globalIdx = findGlobalLineIndex(allLines, source, lineNum);
|
|
1718
|
-
const isDoc = globalIdx >= 0 && isInDocumentationContext(
|
|
1737
|
+
const isDoc = source === "SKILL.md" && globalIdx >= 0 && isInDocumentationContext(
|
|
1719
1738
|
allLines.map((l) => l.line),
|
|
1720
1739
|
globalIdx
|
|
1721
1740
|
);
|
|
1722
|
-
|
|
1741
|
+
const srcLines = getLinesForSource(skill, source);
|
|
1742
|
+
const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);
|
|
1743
|
+
const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);
|
|
1744
|
+
if (isDoc && !inCodeBlock) {
|
|
1745
|
+
} else {
|
|
1723
1746
|
let severity = "HIGH";
|
|
1724
1747
|
let reducedFrom;
|
|
1725
1748
|
let msgSuffix = "";
|
|
1749
|
+
if (inCodeBlock) {
|
|
1750
|
+
const isNearDoc = source === "SKILL.md" && globalIdx >= 0 && isNearDocumentationHeader(
|
|
1751
|
+
allLines.map((l) => l.line),
|
|
1752
|
+
globalIdx
|
|
1753
|
+
);
|
|
1754
|
+
if (isNearDoc) {
|
|
1755
|
+
severity = "LOW";
|
|
1756
|
+
reducedFrom = "HIGH";
|
|
1757
|
+
msgSuffix = " [reduced: in code block within documentation]";
|
|
1758
|
+
} else {
|
|
1759
|
+
const r = reduceSeverity(severity, "in code block");
|
|
1760
|
+
severity = r.severity;
|
|
1761
|
+
reducedFrom = r.reducedFrom;
|
|
1762
|
+
msgSuffix = ` ${r.annotation}`;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
results.push({
|
|
1766
|
+
id: "SUPPLY-003",
|
|
1767
|
+
category: "SUPPLY",
|
|
1768
|
+
severity,
|
|
1769
|
+
title: "Package installation command",
|
|
1770
|
+
message: `${source}:${lineNum}: Installs packages. Verify package names are legitimate.${msgSuffix}`,
|
|
1771
|
+
line: lineNum,
|
|
1772
|
+
snippet: line.trim().slice(0, 120),
|
|
1773
|
+
source,
|
|
1774
|
+
reducedFrom
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
if (GIT_CLONE_PATTERN.test(line)) {
|
|
1779
|
+
const allLines = getAllLines(skill);
|
|
1780
|
+
const globalIdx = findGlobalLineIndex(allLines, source, lineNum);
|
|
1781
|
+
const isDoc = source === "SKILL.md" && globalIdx >= 0 && isInDocumentationContext(
|
|
1782
|
+
allLines.map((l) => l.line),
|
|
1783
|
+
globalIdx
|
|
1784
|
+
);
|
|
1785
|
+
if (!isDoc) {
|
|
1786
|
+
let severity = "MEDIUM";
|
|
1787
|
+
let reducedFrom;
|
|
1788
|
+
let msgSuffix = "";
|
|
1726
1789
|
const srcLines = getLinesForSource(skill, source);
|
|
1727
1790
|
const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);
|
|
1728
1791
|
if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {
|
|
@@ -1732,11 +1795,11 @@ var supplyChainChecks = {
|
|
|
1732
1795
|
msgSuffix = ` ${r.annotation}`;
|
|
1733
1796
|
}
|
|
1734
1797
|
results.push({
|
|
1735
|
-
id: "SUPPLY-
|
|
1798
|
+
id: "SUPPLY-006",
|
|
1736
1799
|
category: "SUPPLY",
|
|
1737
1800
|
severity,
|
|
1738
|
-
title: "
|
|
1739
|
-
message: `${source}:${lineNum}:
|
|
1801
|
+
title: "git clone command",
|
|
1802
|
+
message: `${source}:${lineNum}: Clones a git repository. Verify the source.${msgSuffix}`,
|
|
1740
1803
|
line: lineNum,
|
|
1741
1804
|
snippet: line.trim().slice(0, 120),
|
|
1742
1805
|
source,
|
|
@@ -1744,18 +1807,6 @@ var supplyChainChecks = {
|
|
|
1744
1807
|
});
|
|
1745
1808
|
}
|
|
1746
1809
|
}
|
|
1747
|
-
if (GIT_CLONE_PATTERN.test(line)) {
|
|
1748
|
-
results.push({
|
|
1749
|
-
id: "SUPPLY-006",
|
|
1750
|
-
category: "SUPPLY",
|
|
1751
|
-
severity: "MEDIUM",
|
|
1752
|
-
title: "git clone command",
|
|
1753
|
-
message: `${source}:${lineNum}: Clones a git repository. Verify the source.`,
|
|
1754
|
-
line: lineNum,
|
|
1755
|
-
snippet: line.trim().slice(0, 120),
|
|
1756
|
-
source
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
1810
|
const urls = line.match(URL_PATTERN) || [];
|
|
1760
1811
|
for (const url of urls) {
|
|
1761
1812
|
if (url.startsWith("http://")) {
|