skillscan 0.1.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.
Files changed (81) hide show
  1. package/.eslintrc.json +15 -0
  2. package/README.md +177 -0
  3. package/dist/cli/commands/scan.d.ts +5 -0
  4. package/dist/cli/commands/scan.d.ts.map +1 -0
  5. package/dist/cli/commands/scan.js +67 -0
  6. package/dist/cli/commands/scan.js.map +1 -0
  7. package/dist/cli/index.d.ts +3 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +18 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +30 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/output/formatters.d.ts +3 -0
  16. package/dist/output/formatters.d.ts.map +1 -0
  17. package/dist/output/formatters.js +256 -0
  18. package/dist/output/formatters.js.map +1 -0
  19. package/dist/scanner/engine.d.ts +7 -0
  20. package/dist/scanner/engine.d.ts.map +1 -0
  21. package/dist/scanner/engine.js +119 -0
  22. package/dist/scanner/engine.js.map +1 -0
  23. package/dist/scanner/parsers/skilljson.d.ts +3 -0
  24. package/dist/scanner/parsers/skilljson.d.ts.map +1 -0
  25. package/dist/scanner/parsers/skilljson.js +38 -0
  26. package/dist/scanner/parsers/skilljson.js.map +1 -0
  27. package/dist/scanner/parsers/skillmd.d.ts +3 -0
  28. package/dist/scanner/parsers/skillmd.d.ts.map +1 -0
  29. package/dist/scanner/parsers/skillmd.js +48 -0
  30. package/dist/scanner/parsers/skillmd.js.map +1 -0
  31. package/dist/scanner/rules/file-access.d.ts +11 -0
  32. package/dist/scanner/rules/file-access.d.ts.map +1 -0
  33. package/dist/scanner/rules/file-access.js +76 -0
  34. package/dist/scanner/rules/file-access.js.map +1 -0
  35. package/dist/scanner/rules/hidden-instructions.d.ts +13 -0
  36. package/dist/scanner/rules/hidden-instructions.d.ts.map +1 -0
  37. package/dist/scanner/rules/hidden-instructions.js +88 -0
  38. package/dist/scanner/rules/hidden-instructions.js.map +1 -0
  39. package/dist/scanner/rules/index.d.ts +4 -0
  40. package/dist/scanner/rules/index.d.ts.map +1 -0
  41. package/dist/scanner/rules/index.js +21 -0
  42. package/dist/scanner/rules/index.js.map +1 -0
  43. package/dist/scanner/rules/prompt-injection.d.ts +11 -0
  44. package/dist/scanner/rules/prompt-injection.d.ts.map +1 -0
  45. package/dist/scanner/rules/prompt-injection.js +130 -0
  46. package/dist/scanner/rules/prompt-injection.js.map +1 -0
  47. package/dist/scanner/rules/sensitive-paths.d.ts +11 -0
  48. package/dist/scanner/rules/sensitive-paths.d.ts.map +1 -0
  49. package/dist/scanner/rules/sensitive-paths.js +142 -0
  50. package/dist/scanner/rules/sensitive-paths.js.map +1 -0
  51. package/dist/scoring/trust-score.d.ts +5 -0
  52. package/dist/scoring/trust-score.d.ts.map +1 -0
  53. package/dist/scoring/trust-score.js +35 -0
  54. package/dist/scoring/trust-score.js.map +1 -0
  55. package/dist/types.d.ts +47 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +4 -0
  58. package/dist/types.js.map +1 -0
  59. package/jest.config.js +9 -0
  60. package/package.json +42 -0
  61. package/skill/SKILL.md +76 -0
  62. package/src/cli/commands/scan.ts +35 -0
  63. package/src/cli/index.ts +19 -0
  64. package/src/index.ts +5 -0
  65. package/src/output/formatters.ts +296 -0
  66. package/src/scanner/engine.ts +99 -0
  67. package/src/scanner/parsers/skilljson.ts +37 -0
  68. package/src/scanner/parsers/skillmd.ts +46 -0
  69. package/src/scanner/rules/file-access.ts +78 -0
  70. package/src/scanner/rules/hidden-instructions.ts +92 -0
  71. package/src/scanner/rules/index.ts +20 -0
  72. package/src/scanner/rules/prompt-injection.ts +133 -0
  73. package/src/scanner/rules/sensitive-paths.ts +144 -0
  74. package/src/scoring/trust-score.ts +34 -0
  75. package/src/types.ts +54 -0
  76. package/tests/fixtures/malicious-skill/SKILL.md +26 -0
  77. package/tests/fixtures/safe-skill/SKILL.md +25 -0
  78. package/tests/rules/prompt-injection.test.ts +123 -0
  79. package/tests/rules/sensitive-paths.test.ts +115 -0
  80. package/tests/scoring/trust-score.test.ts +100 -0
  81. package/tsconfig.json +19 -0
@@ -0,0 +1,11 @@
1
+ import { Rule, Finding, SkillMetadata, Severity } from '../../types';
2
+ export declare class SensitivePathsRule implements Rule {
3
+ id: string;
4
+ name: string;
5
+ description: string;
6
+ severity: Severity;
7
+ private patterns;
8
+ check(content: string, _metadata: SkillMetadata, filePath: string): Finding[];
9
+ private getLineNumber;
10
+ }
11
+ //# sourceMappingURL=sensitive-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitive-paths.d.ts","sourceRoot":"","sources":["../../../src/scanner/rules/sensitive-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAErE,qBAAa,kBAAmB,YAAW,IAAI;IAC7C,EAAE,SAAqB;IACvB,IAAI,SAA+B;IACnC,WAAW,SAAuE;IAClF,QAAQ,EAAE,QAAQ,CAAc;IAEhC,OAAO,CAAC,QAAQ,CAuGd;IAEF,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE;IA2B7E,OAAO,CAAC,aAAa;CAGtB"}
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SensitivePathsRule = void 0;
4
+ class SensitivePathsRule {
5
+ constructor() {
6
+ this.id = 'sensitive-paths';
7
+ this.name = 'Sensitive Path References';
8
+ this.description = 'Detects references to sensitive system paths and credential files';
9
+ this.severity = 'CRITICAL';
10
+ this.patterns = [
11
+ // SSH
12
+ {
13
+ name: 'ssh-keys',
14
+ regex: /~?\/?\.ssh\/(id_rsa|id_ed25519|id_ecdsa|id_dsa|authorized_keys|known_hosts|config)/gi,
15
+ severity: 'CRITICAL',
16
+ message: 'SSH key/config path referenced'
17
+ },
18
+ {
19
+ name: 'ssh-directory',
20
+ regex: /~?\/?\.ssh\/?(?:\*|$)/gi,
21
+ severity: 'CRITICAL',
22
+ message: 'SSH directory referenced'
23
+ },
24
+ // AWS
25
+ {
26
+ name: 'aws-credentials',
27
+ regex: /~?\/?\.aws\/(credentials|config)/gi,
28
+ severity: 'CRITICAL',
29
+ message: 'AWS credentials file referenced'
30
+ },
31
+ {
32
+ name: 'aws-env-vars',
33
+ regex: /AWS_(SECRET_ACCESS_KEY|ACCESS_KEY_ID|SESSION_TOKEN)/gi,
34
+ severity: 'CRITICAL',
35
+ message: 'AWS credential environment variable referenced'
36
+ },
37
+ // GCP
38
+ {
39
+ name: 'gcp-credentials',
40
+ regex: /GOOGLE_APPLICATION_CREDENTIALS|~?\/?\.config\/gcloud/gi,
41
+ severity: 'CRITICAL',
42
+ message: 'GCP credential path/variable referenced'
43
+ },
44
+ // Azure
45
+ {
46
+ name: 'azure-credentials',
47
+ regex: /AZURE_(CLIENT_SECRET|TENANT_ID|CLIENT_ID)|~?\/?\.azure/gi,
48
+ severity: 'CRITICAL',
49
+ message: 'Azure credential path/variable referenced'
50
+ },
51
+ // GPG/PGP
52
+ {
53
+ name: 'gpg-keys',
54
+ regex: /~?\/?\.gnupg\/?|\.gpg\b|secring\.gpg/gi,
55
+ severity: 'HIGH',
56
+ message: 'GPG key path referenced'
57
+ },
58
+ // Generic secrets
59
+ {
60
+ name: 'env-files',
61
+ regex: /\.env(\.local|\.prod|\.production|\.development|\.staging)?(?:\b|$)/gi,
62
+ severity: 'HIGH',
63
+ message: 'Environment file referenced'
64
+ },
65
+ {
66
+ name: 'password-files',
67
+ regex: /\/etc\/passwd|\/etc\/shadow|\.password|\.secret/gi,
68
+ severity: 'CRITICAL',
69
+ message: 'Password/secret file referenced'
70
+ },
71
+ // Browser data
72
+ {
73
+ name: 'browser-data',
74
+ regex: /~?\/?\.config\/(google-chrome|chromium|BraveSoftware)\/|Library\/Application Support\/(Google\/Chrome|BraveSoftware)/gi,
75
+ severity: 'HIGH',
76
+ message: 'Browser profile data referenced'
77
+ },
78
+ // Token patterns
79
+ {
80
+ name: 'api-tokens',
81
+ regex: /(API_KEY|API_SECRET|AUTH_TOKEN|PRIVATE_KEY|SECRET_KEY|ACCESS_TOKEN)(?:\s*=|\s*:)/gi,
82
+ severity: 'HIGH',
83
+ message: 'API token/key variable pattern detected'
84
+ },
85
+ // Kubernetes
86
+ {
87
+ name: 'kube-config',
88
+ regex: /~?\/?\.kube\/config|KUBECONFIG/gi,
89
+ severity: 'HIGH',
90
+ message: 'Kubernetes config referenced'
91
+ },
92
+ // Docker
93
+ {
94
+ name: 'docker-config',
95
+ regex: /~?\/?\.docker\/config\.json/gi,
96
+ severity: 'HIGH',
97
+ message: 'Docker config (may contain registry auth) referenced'
98
+ },
99
+ // NPM
100
+ {
101
+ name: 'npm-tokens',
102
+ regex: /~?\/?\.npmrc|NPM_TOKEN/gi,
103
+ severity: 'HIGH',
104
+ message: 'NPM config/token referenced'
105
+ },
106
+ // Git credentials
107
+ {
108
+ name: 'git-credentials',
109
+ regex: /~?\/?\.git-credentials|~?\/?\.gitconfig/gi,
110
+ severity: 'HIGH',
111
+ message: 'Git credentials/config referenced'
112
+ }
113
+ ];
114
+ }
115
+ check(content, _metadata, filePath) {
116
+ const findings = [];
117
+ const lines = content.split('\n');
118
+ for (const pattern of this.patterns) {
119
+ let match;
120
+ const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
121
+ while ((match = regex.exec(content)) !== null) {
122
+ const lineNumber = this.getLineNumber(content, match.index);
123
+ const contextLine = lines[lineNumber - 1] || '';
124
+ findings.push({
125
+ ruleId: this.id,
126
+ ruleName: this.name,
127
+ severity: pattern.severity,
128
+ message: `${pattern.message}: "${match[0]}"`,
129
+ file: filePath,
130
+ line: lineNumber,
131
+ context: contextLine.trim().slice(0, 100)
132
+ });
133
+ }
134
+ }
135
+ return findings;
136
+ }
137
+ getLineNumber(content, index) {
138
+ return content.slice(0, index).split('\n').length;
139
+ }
140
+ }
141
+ exports.SensitivePathsRule = SensitivePathsRule;
142
+ //# sourceMappingURL=sensitive-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitive-paths.js","sourceRoot":"","sources":["../../../src/scanner/rules/sensitive-paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { Finding } from '../types';
2
+ export declare function calculateScore(findings: Finding[]): number;
3
+ export declare function getRating(score: number): 'VERIFIED' | 'CAUTION' | 'WARNING';
4
+ export declare function getRatingEmoji(rating: 'VERIFIED' | 'CAUTION' | 'WARNING'): string;
5
+ //# sourceMappingURL=trust-score.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-score.d.ts","sourceRoot":"","sources":["../../src/scoring/trust-score.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,UAAU,CAAC;AAS7C,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAU1D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAI3E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAMjF"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateScore = calculateScore;
4
+ exports.getRating = getRating;
5
+ exports.getRatingEmoji = getRatingEmoji;
6
+ const SEVERITY_WEIGHTS = {
7
+ CRITICAL: 40,
8
+ HIGH: 20,
9
+ MEDIUM: 10,
10
+ LOW: 5
11
+ };
12
+ function calculateScore(findings) {
13
+ const baseScore = 100;
14
+ let deductions = 0;
15
+ for (const finding of findings) {
16
+ deductions += SEVERITY_WEIGHTS[finding.severity];
17
+ }
18
+ // Floor at 0, ceiling at 100
19
+ return Math.max(0, Math.min(100, baseScore - deductions));
20
+ }
21
+ function getRating(score) {
22
+ if (score >= 80)
23
+ return 'VERIFIED';
24
+ if (score >= 50)
25
+ return 'CAUTION';
26
+ return 'WARNING';
27
+ }
28
+ function getRatingEmoji(rating) {
29
+ switch (rating) {
30
+ case 'VERIFIED': return '🟢';
31
+ case 'CAUTION': return '🟡';
32
+ case 'WARNING': return '🔴';
33
+ }
34
+ }
35
+ //# sourceMappingURL=trust-score.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-score.js","sourceRoot":"","sources":["../../src/scoring/trust-score.ts"],"names":[],"mappings":";;AASA,wCAUC;AAED,8BAIC;AAED,wCAMC;AA/BD,MAAM,gBAAgB,GAA6B;IACjD,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,EAAE;IACR,MAAM,EAAE,EAAE;IACV,GAAG,EAAE,CAAC;CACP,CAAC;AAEF,SAAgB,cAAc,CAAC,QAAmB;IAChD,MAAM,SAAS,GAAG,GAAG,CAAC;IAEtB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,UAAU,IAAI,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED,6BAA6B;IAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,SAAgB,SAAS,CAAC,KAAa;IACrC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,UAAU,CAAC;IACnC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAClC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,cAAc,CAAC,MAA0C;IACvE,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,UAAU,CAAC,CAAC,OAAO,IAAI,CAAC;QAC7B,KAAK,SAAS,CAAC,CAAC,OAAO,IAAI,CAAC;QAC5B,KAAK,SAAS,CAAC,CAAC,OAAO,IAAI,CAAC;IAC9B,CAAC;AACH,CAAC"}
@@ -0,0 +1,47 @@
1
+ export type Severity = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
2
+ export interface Finding {
3
+ ruleId: string;
4
+ ruleName: string;
5
+ severity: Severity;
6
+ message: string;
7
+ file: string;
8
+ line?: number;
9
+ column?: number;
10
+ context?: string;
11
+ }
12
+ export interface ScanResult {
13
+ skillPath: string;
14
+ skillName: string;
15
+ findings: Finding[];
16
+ score: number;
17
+ rating: 'VERIFIED' | 'CAUTION' | 'WARNING';
18
+ scannedAt: string;
19
+ scanDuration: number;
20
+ }
21
+ export interface SkillMetadata {
22
+ name: string;
23
+ description?: string;
24
+ version?: string;
25
+ author?: string;
26
+ tools?: ToolDefinition[];
27
+ rawContent: string;
28
+ frontmatter: Record<string, unknown>;
29
+ }
30
+ export interface ToolDefinition {
31
+ name: string;
32
+ description?: string;
33
+ parameters?: Record<string, unknown>;
34
+ }
35
+ export interface Rule {
36
+ id: string;
37
+ name: string;
38
+ description: string;
39
+ severity: Severity;
40
+ check(content: string, metadata: SkillMetadata, filePath: string): Finding[];
41
+ }
42
+ export interface ScanOptions {
43
+ path: string;
44
+ format?: 'json' | 'text' | 'ci';
45
+ verbose?: boolean;
46
+ }
47
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE9D,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;CAC9E;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Core types for SkillScan
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,2BAA2B"}
package/jest.config.js ADDED
@@ -0,0 +1,9 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ testMatch: ['**/tests/**/*.test.ts'],
6
+ collectCoverageFrom: ['src/**/*.ts'],
7
+ coverageDirectory: 'coverage',
8
+ verbose: true
9
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "skillscan",
3
+ "version": "0.1.0",
4
+ "description": "Trust verification layer for AI agent skill marketplaces - scan, score, and verify skills before installation",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "skillscan": "./dist/cli/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "ts-node src/cli/index.ts",
13
+ "test": "jest",
14
+ "test:watch": "jest --watch",
15
+ "lint": "eslint src/**/*.ts",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": ["security", "ai", "agent", "skills", "mcp", "scanner", "trust", "verification"],
19
+ "author": "dejimarquis",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/dejimarquis/SkillScan"
24
+ },
25
+ "devDependencies": {
26
+ "@types/jest": "^30.0.0",
27
+ "@types/node": "^25.2.0",
28
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
29
+ "@typescript-eslint/parser": "^8.54.0",
30
+ "eslint": "^9.39.2",
31
+ "jest": "^30.2.0",
32
+ "prettier": "^3.8.1",
33
+ "ts-jest": "^29.4.6",
34
+ "ts-node": "^10.9.2",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "dependencies": {
38
+ "chalk": "^5.6.2",
39
+ "commander": "^14.0.3",
40
+ "gray-matter": "^4.0.3"
41
+ }
42
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: skillscan
3
+ description: Security scanner for AI agent skills - detect prompt injection, data exfiltration, and supply chain attacks before installation
4
+ version: 0.1.0
5
+ author: dejimarquis
6
+ tags:
7
+ - security
8
+ - scanning
9
+ - trust
10
+ - verification
11
+ ---
12
+
13
+ # SkillScan 🔍
14
+
15
+ **Trust verification for AI agent skills** — Scan, score, and verify skills before installation.
16
+
17
+ ## Why Use SkillScan?
18
+
19
+ AI agent skill marketplaces are growing rapidly, but **skill supply chain security is broken**:
20
+
21
+ - 🎭 **Tool Poisoning**: Hidden instructions in skill metadata can exfiltrate SSH keys, API tokens, or personal data
22
+ - 📈 **Fake Trust Signals**: Download counts can be inflated with simple scripts
23
+ - 💉 **Prompt Injection**: Malicious skills can override agent behavior
24
+
25
+ SkillScan detects these attacks **before they reach your agent**.
26
+
27
+ ## How to Scan a Skill
28
+
29
+ When a user asks you to check if a skill is safe, run this command:
30
+
31
+ ```bash
32
+ npx skillscan scan /path/to/skill
33
+ ```
34
+
35
+ For more technical details (file locations, line numbers):
36
+
37
+ ```bash
38
+ npx skillscan scan /path/to/skill --verbose
39
+ ```
40
+
41
+ ### Example prompts users might ask:
42
+ - "Scan the github-automator skill for security issues"
43
+ - "Check if this skill is safe before I install it"
44
+ - "Run a security scan on /path/to/some-skill"
45
+
46
+ ## Understanding the Output
47
+
48
+ **For safe skills:** You'll see a green "SAFE TO INSTALL" verdict with a 100/100 score.
49
+
50
+ **For suspicious skills:** You'll see a red "DO NOT INSTALL" verdict with:
51
+ - A TL;DR explaining the main risk
52
+ - Grouped issues with code snippets showing what's wrong
53
+ - A plain-English explanation of the risks
54
+ - A clear recommendation
55
+
56
+ ## What It Detects
57
+
58
+ ### 🔴 Critical Issues
59
+ - **Prompt Injection**: Attempts to override AI safety rules
60
+ - **Credential Theft**: References to SSH keys, AWS credentials, API tokens
61
+
62
+ ### 🟠 High Risk Issues
63
+ - **Hidden Instructions**: Zero-width characters, HTML comments hiding code
64
+ - **Suspicious File Access**: Path traversal, home directory access
65
+
66
+ ## Trust Score
67
+
68
+ | Score | Verdict | What to tell the user |
69
+ |-------|---------|----------------------|
70
+ | 80-100 | ✅ SAFE | "This skill looks safe to install" |
71
+ | 50-79 | 🟡 REVIEW | "Review the flagged items before installing" |
72
+ | 0-49 | 🔴 UNSAFE | "Do not install this skill - it shows signs of malicious behavior" |
73
+
74
+ ---
75
+
76
+ **SkillScan** — Because trust should be verified, not assumed. 🛡️
@@ -0,0 +1,35 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { ScanEngine } from '../../scanner/engine';
4
+ import { formatOutput } from '../../output/formatters';
5
+ import { ScanOptions } from '../../types';
6
+
7
+ export async function scanCommand(skillPath: string, options: { format: string; verbose: boolean }): Promise<void> {
8
+ const resolvedPath = path.resolve(skillPath);
9
+
10
+ if (!fs.existsSync(resolvedPath)) {
11
+ console.error(`Error: Path not found: ${resolvedPath}`);
12
+ process.exit(1);
13
+ }
14
+
15
+ const scanOptions: ScanOptions = {
16
+ path: resolvedPath,
17
+ format: options.format as 'json' | 'text' | 'ci',
18
+ verbose: options.verbose
19
+ };
20
+
21
+ try {
22
+ const engine = new ScanEngine();
23
+ const result = await engine.scan(scanOptions);
24
+ const output = formatOutput(result, scanOptions);
25
+ console.log(output);
26
+
27
+ // Exit with error code if findings are critical
28
+ if (result.rating === 'WARNING') {
29
+ process.exit(1);
30
+ }
31
+ } catch (error) {
32
+ console.error(`Error scanning skill: ${error instanceof Error ? error.message : error}`);
33
+ process.exit(1);
34
+ }
35
+ }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { scanCommand } from './commands/scan';
4
+
5
+ const program = new Command();
6
+
7
+ program
8
+ .name('skillscan')
9
+ .description('Trust verification for AI agent skills - scan, score, and verify before installation')
10
+ .version('0.1.0');
11
+
12
+ program
13
+ .command('scan <path>')
14
+ .description('Scan a skill directory or file for security issues')
15
+ .option('-f, --format <format>', 'Output format: json, text, ci', 'text')
16
+ .option('-v, --verbose', 'Show detailed output', false)
17
+ .action(scanCommand);
18
+
19
+ program.parse();
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { ScanEngine } from './scanner/engine';
2
+ export { calculateScore, getRating, getRatingEmoji } from './scoring/trust-score';
3
+ export { formatOutput } from './output/formatters';
4
+ export { getAllRules, getRuleById } from './scanner/rules';
5
+ export * from './types';