@skillctl/security 0.3.1 → 0.4.0

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/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { runAudit, auditExitCode } from './runner.js';
2
+ export { validateSkillDir } from './validate-skill.js';
2
3
  export type { AuditReport, AuditFinding, AuditSeverity } from './types.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { runAudit, auditExitCode } from './runner.js';
2
+ export { validateSkillDir } from './validate-skill.js';
2
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validate-skill.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-skill.test.d.ts","sourceRoot":"","sources":["../../src/test/validate-skill.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ import { validateSkillDir } from '../validate-skill.js';
7
+ test('validateSkillDir passes for valid skill directory', async () => {
8
+ const root = await mkdtemp(join(tmpdir(), 'validate-skill-'));
9
+ const skillDir = join(root, 'demo-skill');
10
+ try {
11
+ await mkdir(skillDir, { recursive: true });
12
+ await writeFile(join(skillDir, 'SKILL.md'), '---\nname: demo-skill\n---\n# Demo\n');
13
+ const report = await validateSkillDir(skillDir);
14
+ assert.equal(report.status, 'ok');
15
+ assert.equal(report.scanned, 1);
16
+ assert.equal(report.findings.length, 0);
17
+ }
18
+ finally {
19
+ await rm(root, { recursive: true, force: true });
20
+ }
21
+ });
22
+ test('validateSkillDir errors when SKILL.md is missing', async () => {
23
+ const root = await mkdtemp(join(tmpdir(), 'validate-skill-'));
24
+ const skillDir = join(root, 'empty-skill');
25
+ try {
26
+ await mkdir(skillDir, { recursive: true });
27
+ const report = await validateSkillDir(skillDir);
28
+ assert.equal(report.status, 'errors');
29
+ assert.ok(report.findings.some((f) => f.rule === 'skill-md-missing'));
30
+ }
31
+ finally {
32
+ await rm(root, { recursive: true, force: true });
33
+ }
34
+ });
35
+ //# sourceMappingURL=validate-skill.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-skill.test.js","sourceRoot":"","sources":["../../src/test/validate-skill.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,IAAI,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;IACnE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,sCAAsC,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;IAClE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC;IACxE,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { AuditReport } from './types.js';
2
+ /** Validate a single skill directory (SKILL.md layout) without a project lockfile. */
3
+ export declare function validateSkillDir(skillPath: string): Promise<AuditReport>;
4
+ //# sourceMappingURL=validate-skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-skill.d.ts","sourceRoot":"","sources":["../src/validate-skill.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,YAAY,CAAC;AAkB5D,sFAAsF;AACtF,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAkE9E"}
@@ -0,0 +1,83 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { basename, join, resolve } from 'node:path';
3
+ import { canonicalizeName } from '@skillctl/core';
4
+ import { checkNameDirMatch } from './rules/name-dir-match.js';
5
+ import { checkScriptHeuristics } from './rules/script-heuristics.js';
6
+ import { checkPathTraversal } from './rules/path-traversal.js';
7
+ import { checkSizeLimits } from './rules/size-limits.js';
8
+ async function findSkillMarkdown(skillPath) {
9
+ for (const f of ['SKILL.md', 'skill.md']) {
10
+ try {
11
+ await stat(join(skillPath, f));
12
+ return true;
13
+ }
14
+ catch {
15
+ // continue
16
+ }
17
+ }
18
+ return false;
19
+ }
20
+ /** Validate a single skill directory (SKILL.md layout) without a project lockfile. */
21
+ export async function validateSkillDir(skillPath) {
22
+ const abs = resolve(skillPath);
23
+ let dirStat;
24
+ try {
25
+ dirStat = await stat(abs);
26
+ }
27
+ catch {
28
+ const name = canonicalizeName(basename(abs));
29
+ return {
30
+ status: 'errors',
31
+ scanned: 0,
32
+ findings: [
33
+ {
34
+ rule: 'skill-dir-missing',
35
+ severity: 'error',
36
+ skill: name,
37
+ message: `Skill directory not found: ${abs}`,
38
+ path: abs,
39
+ },
40
+ ],
41
+ };
42
+ }
43
+ if (!dirStat.isDirectory()) {
44
+ const name = canonicalizeName(basename(abs));
45
+ return {
46
+ status: 'errors',
47
+ scanned: 0,
48
+ findings: [
49
+ {
50
+ rule: 'skill-dir-missing',
51
+ severity: 'error',
52
+ skill: name,
53
+ message: 'Path is not a directory',
54
+ path: abs,
55
+ },
56
+ ],
57
+ };
58
+ }
59
+ const name = canonicalizeName(basename(abs));
60
+ const findings = [];
61
+ if (!(await findSkillMarkdown(abs))) {
62
+ findings.push({
63
+ rule: 'skill-md-missing',
64
+ severity: 'error',
65
+ skill: name,
66
+ message: 'SKILL.md (or skill.md) not found',
67
+ path: abs,
68
+ });
69
+ return { status: 'errors', findings, scanned: 0 };
70
+ }
71
+ findings.push(...(await checkNameDirMatch(name, abs)));
72
+ findings.push(...(await checkScriptHeuristics(name, abs)));
73
+ findings.push(...(await checkPathTraversal(name, abs)));
74
+ findings.push(...(await checkSizeLimits(name, abs)));
75
+ const hasError = findings.some((f) => f.severity === 'error');
76
+ const hasWarning = findings.some((f) => f.severity === 'warning');
77
+ return {
78
+ status: hasError ? 'errors' : hasWarning ? 'warnings' : 'ok',
79
+ findings,
80
+ scanned: 1,
81
+ };
82
+ }
83
+ //# sourceMappingURL=validate-skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-skill.js","sourceRoot":"","sources":["../src/validate-skill.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillctl/security",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Security scanner and audit for skillctl skills",
5
5
  "license": "MIT",
6
6
  "author": "xFurti & Gabry848",
@@ -11,8 +11,8 @@
11
11
  "dist"
12
12
  ],
13
13
  "dependencies": {
14
- "@skillctl/core": "0.3.1",
15
- "@skillctl/lockfile": "0.3.1"
14
+ "@skillctl/core": "0.4.0",
15
+ "@skillctl/lockfile": "0.4.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "^20.14.0",