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,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ScanEngine = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const skillmd_1 = require("./parsers/skillmd");
40
+ const skilljson_1 = require("./parsers/skilljson");
41
+ const rules_1 = require("./rules");
42
+ const trust_score_1 = require("../scoring/trust-score");
43
+ class ScanEngine {
44
+ async scan(options) {
45
+ const startTime = Date.now();
46
+ const { path: skillPath } = options;
47
+ const stats = fs.statSync(skillPath);
48
+ const isDirectory = stats.isDirectory();
49
+ const filesToScan = isDirectory
50
+ ? this.discoverFiles(skillPath)
51
+ : [skillPath];
52
+ const allFindings = [];
53
+ let skillName = path.basename(skillPath);
54
+ for (const file of filesToScan) {
55
+ const content = fs.readFileSync(file, 'utf-8');
56
+ const metadata = this.parseFile(file, content);
57
+ if (metadata.name) {
58
+ skillName = metadata.name;
59
+ }
60
+ const rules = (0, rules_1.getAllRules)();
61
+ for (const rule of rules) {
62
+ const findings = rule.check(content, metadata, file);
63
+ allFindings.push(...findings);
64
+ }
65
+ }
66
+ const score = (0, trust_score_1.calculateScore)(allFindings);
67
+ const rating = (0, trust_score_1.getRating)(score);
68
+ return {
69
+ skillPath,
70
+ skillName,
71
+ findings: allFindings,
72
+ score,
73
+ rating,
74
+ scannedAt: new Date().toISOString(),
75
+ scanDuration: Date.now() - startTime
76
+ };
77
+ }
78
+ discoverFiles(dirPath) {
79
+ const files = [];
80
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
81
+ for (const entry of entries) {
82
+ const fullPath = path.join(dirPath, entry.name);
83
+ if (entry.isDirectory()) {
84
+ // Skip node_modules and hidden directories
85
+ if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
86
+ files.push(...this.discoverFiles(fullPath));
87
+ }
88
+ }
89
+ else {
90
+ // Include relevant skill files
91
+ const ext = path.extname(entry.name).toLowerCase();
92
+ const name = entry.name.toLowerCase();
93
+ if (name === 'skill.md' || name === 'skill.json' ||
94
+ ext === '.md' || ext === '.json' || ext === '.yaml' || ext === '.yml') {
95
+ files.push(fullPath);
96
+ }
97
+ }
98
+ }
99
+ return files;
100
+ }
101
+ parseFile(filePath, content) {
102
+ const ext = path.extname(filePath).toLowerCase();
103
+ const name = path.basename(filePath).toLowerCase();
104
+ if (name === 'skill.md' || ext === '.md') {
105
+ return (0, skillmd_1.parseSkillMd)(content);
106
+ }
107
+ if (name === 'skill.json' || ext === '.json') {
108
+ return (0, skilljson_1.parseSkillJson)(content);
109
+ }
110
+ // Default metadata for other files
111
+ return {
112
+ name: path.basename(filePath),
113
+ rawContent: content,
114
+ frontmatter: {}
115
+ };
116
+ }
117
+ }
118
+ exports.ScanEngine = ScanEngine;
119
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/scanner/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { SkillMetadata } from '../../types';
2
+ export declare function parseSkillJson(content: string): SkillMetadata;
3
+ //# sourceMappingURL=skilljson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skilljson.d.ts","sourceRoot":"","sources":["../../../src/scanner/parsers/skilljson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,MAAM,aAAa,CAAC;AAE5D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAkC7D"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseSkillJson = parseSkillJson;
4
+ function parseSkillJson(content) {
5
+ try {
6
+ const json = JSON.parse(content);
7
+ const tools = [];
8
+ if (json.tools && Array.isArray(json.tools)) {
9
+ for (const tool of json.tools) {
10
+ if (typeof tool === 'object' && tool.name) {
11
+ tools.push({
12
+ name: tool.name,
13
+ description: tool.description,
14
+ parameters: tool.parameters
15
+ });
16
+ }
17
+ }
18
+ }
19
+ return {
20
+ name: json.name || '',
21
+ description: json.description,
22
+ version: json.version,
23
+ author: json.author,
24
+ tools,
25
+ rawContent: content,
26
+ frontmatter: json
27
+ };
28
+ }
29
+ catch {
30
+ // Invalid JSON, return minimal metadata
31
+ return {
32
+ name: '',
33
+ rawContent: content,
34
+ frontmatter: {}
35
+ };
36
+ }
37
+ }
38
+ //# sourceMappingURL=skilljson.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skilljson.js","sourceRoot":"","sources":["../../../src/scanner/parsers/skilljson.ts"],"names":[],"mappings":";;AAEA,wCAkCC;AAlCD,SAAgB,cAAc,CAAC,OAAe;IAC5C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEjC,MAAM,KAAK,GAAqB,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBAC1C,KAAK,CAAC,IAAI,CAAC;wBACT,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,WAAW,EAAE,IAAI,CAAC,WAAW;wBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;qBAC5B,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;YACrB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,UAAU,EAAE,OAAO;YACnB,WAAW,EAAE,IAAI;SAClB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;QACxC,OAAO;YACL,IAAI,EAAE,EAAE;YACR,UAAU,EAAE,OAAO;YACnB,WAAW,EAAE,EAAE;SAChB,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { SkillMetadata } from '../../types';
2
+ export declare function parseSkillMd(content: string): SkillMetadata;
3
+ //# sourceMappingURL=skillmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillmd.d.ts","sourceRoot":"","sources":["../../../src/scanner/parsers/skillmd.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAkB,MAAM,aAAa,CAAC;AAE5D,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CA0B3D"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseSkillMd = parseSkillMd;
7
+ const gray_matter_1 = __importDefault(require("gray-matter"));
8
+ function parseSkillMd(content) {
9
+ const { data: frontmatter, content: bodyContent } = (0, gray_matter_1.default)(content);
10
+ // Extract tools from frontmatter if present
11
+ const tools = [];
12
+ if (frontmatter.tools && Array.isArray(frontmatter.tools)) {
13
+ for (const tool of frontmatter.tools) {
14
+ if (typeof tool === 'object' && tool.name) {
15
+ tools.push({
16
+ name: tool.name,
17
+ description: tool.description,
18
+ parameters: tool.parameters
19
+ });
20
+ }
21
+ }
22
+ }
23
+ return {
24
+ name: frontmatter.name || frontmatter.title || '',
25
+ description: frontmatter.description || extractFirstParagraph(bodyContent),
26
+ version: frontmatter.version,
27
+ author: frontmatter.author,
28
+ tools,
29
+ rawContent: content,
30
+ frontmatter
31
+ };
32
+ }
33
+ function extractFirstParagraph(content) {
34
+ const lines = content.split('\n');
35
+ const paragraphLines = [];
36
+ for (const line of lines) {
37
+ const trimmed = line.trim();
38
+ if (trimmed.startsWith('#'))
39
+ continue; // Skip headers
40
+ if (trimmed === '' && paragraphLines.length > 0)
41
+ break;
42
+ if (trimmed !== '') {
43
+ paragraphLines.push(trimmed);
44
+ }
45
+ }
46
+ return paragraphLines.join(' ').slice(0, 200);
47
+ }
48
+ //# sourceMappingURL=skillmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skillmd.js","sourceRoot":"","sources":["../../../src/scanner/parsers/skillmd.ts"],"names":[],"mappings":";;;;;AAGA,oCA0BC;AA7BD,8DAAiC;AAGjC,SAAgB,YAAY,CAAC,OAAe;IAC1C,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,IAAA,qBAAM,EAAC,OAAO,CAAC,CAAC;IAEpE,4CAA4C;IAC5C,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,IAAI,WAAW,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC1C,KAAK,CAAC,IAAI,CAAC;oBACT,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,KAAK,IAAI,EAAE;QACjD,WAAW,EAAE,WAAW,CAAC,WAAW,IAAI,qBAAqB,CAAC,WAAW,CAAC;QAC1E,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,KAAK;QACL,UAAU,EAAE,OAAO;QACnB,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,OAAe;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS,CAAC,eAAe;QACtD,IAAI,OAAO,KAAK,EAAE,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM;QACvD,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;YACnB,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Rule, Finding, SkillMetadata, Severity } from '../../types';
2
+ export declare class FileAccessRule 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=file-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-access.d.ts","sourceRoot":"","sources":["../../../src/scanner/rules/file-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAErE,qBAAa,cAAe,YAAW,IAAI;IACzC,EAAE,SAAiB;IACnB,IAAI,SAAsC;IAC1C,WAAW,SAAwE;IACnF,QAAQ,EAAE,QAAQ,CAAU;IAE5B,OAAO,CAAC,QAAQ,CAqCd;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,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileAccessRule = void 0;
4
+ class FileAccessRule {
5
+ constructor() {
6
+ this.id = 'file-access';
7
+ this.name = 'Suspicious File Access Detection';
8
+ this.description = 'Detects suspicious file access patterns that could exfiltrate data';
9
+ this.severity = 'HIGH';
10
+ this.patterns = [
11
+ {
12
+ name: 'path-traversal',
13
+ regex: /\.\.\/|\.\.\\|\.\.[\/\\]/gi,
14
+ severity: 'HIGH',
15
+ message: 'Path traversal pattern detected - may access files outside intended directory'
16
+ },
17
+ {
18
+ name: 'home-directory-access',
19
+ regex: /~\/|%HOME%|%USERPROFILE%|\$HOME/gi,
20
+ severity: 'HIGH',
21
+ message: 'Home directory access pattern detected'
22
+ },
23
+ {
24
+ name: 'wildcard-read',
25
+ regex: /\*\.\w{2,4}|\*\*\/\*|glob\s*\(/gi,
26
+ severity: 'MEDIUM',
27
+ message: 'Wildcard file pattern detected - may sweep multiple files'
28
+ },
29
+ {
30
+ name: 'sensitive-file-extensions',
31
+ regex: /\.(pem|key|crt|pfx|p12|jks|keystore|env|credentials)\b/gi,
32
+ severity: 'HIGH',
33
+ message: 'Reference to sensitive file type detected'
34
+ },
35
+ {
36
+ name: 'read-file-operations',
37
+ regex: /readFile|readFileSync|fs\.read|open\s*\(|fopen|file_get_contents/gi,
38
+ severity: 'LOW',
39
+ message: 'File read operation detected - verify intended behavior'
40
+ },
41
+ {
42
+ name: 'network-after-read',
43
+ regex: /(fetch|axios|request|http\.|https\.|XMLHttpRequest|curl|wget)/gi,
44
+ severity: 'MEDIUM',
45
+ message: 'Network operation detected - check if combined with file access'
46
+ }
47
+ ];
48
+ }
49
+ check(content, _metadata, filePath) {
50
+ const findings = [];
51
+ const lines = content.split('\n');
52
+ for (const pattern of this.patterns) {
53
+ let match;
54
+ const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
55
+ while ((match = regex.exec(content)) !== null) {
56
+ const lineNumber = this.getLineNumber(content, match.index);
57
+ const contextLine = lines[lineNumber - 1] || '';
58
+ findings.push({
59
+ ruleId: this.id,
60
+ ruleName: this.name,
61
+ severity: pattern.severity,
62
+ message: `${pattern.message}: "${match[0]}"`,
63
+ file: filePath,
64
+ line: lineNumber,
65
+ context: contextLine.trim().slice(0, 100)
66
+ });
67
+ }
68
+ }
69
+ return findings;
70
+ }
71
+ getLineNumber(content, index) {
72
+ return content.slice(0, index).split('\n').length;
73
+ }
74
+ }
75
+ exports.FileAccessRule = FileAccessRule;
76
+ //# sourceMappingURL=file-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-access.js","sourceRoot":"","sources":["../../../src/scanner/rules/file-access.ts"],"names":[],"mappings":";;;AAEA,MAAa,cAAc;IAA3B;QACE,OAAE,GAAG,aAAa,CAAC;QACnB,SAAI,GAAG,kCAAkC,CAAC;QAC1C,gBAAW,GAAG,oEAAoE,CAAC;QACnF,aAAQ,GAAa,MAAM,CAAC;QAEpB,aAAQ,GAAG;YACjB;gBACE,IAAI,EAAE,gBAAgB;gBACtB,KAAK,EAAE,4BAA4B;gBACnC,QAAQ,EAAE,MAAkB;gBAC5B,OAAO,EAAE,+EAA+E;aACzF;YACD;gBACE,IAAI,EAAE,uBAAuB;gBAC7B,KAAK,EAAE,mCAAmC;gBAC1C,QAAQ,EAAE,MAAkB;gBAC5B,OAAO,EAAE,wCAAwC;aAClD;YACD;gBACE,IAAI,EAAE,eAAe;gBACrB,KAAK,EAAE,kCAAkC;gBACzC,QAAQ,EAAE,QAAoB;gBAC9B,OAAO,EAAE,2DAA2D;aACrE;YACD;gBACE,IAAI,EAAE,2BAA2B;gBACjC,KAAK,EAAE,0DAA0D;gBACjE,QAAQ,EAAE,MAAkB;gBAC5B,OAAO,EAAE,2CAA2C;aACrD;YACD;gBACE,IAAI,EAAE,sBAAsB;gBAC5B,KAAK,EAAE,oEAAoE;gBAC3E,QAAQ,EAAE,KAAiB;gBAC3B,OAAO,EAAE,yDAAyD;aACnE;YACD;gBACE,IAAI,EAAE,oBAAoB;gBAC1B,KAAK,EAAE,iEAAiE;gBACxE,QAAQ,EAAE,QAAoB;gBAC9B,OAAO,EAAE,iEAAiE;aAC3E;SACF,CAAC;IAgCJ,CAAC;IA9BC,KAAK,CAAC,OAAe,EAAE,SAAwB,EAAE,QAAgB;QAC/D,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC;YACV,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAEpE,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC5D,MAAM,WAAW,GAAG,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;gBAEhD,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,MAAM,KAAK,CAAC,CAAC,CAAC,GAAG;oBAC5C,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;iBAC1C,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,aAAa,CAAC,OAAe,EAAE,KAAa;QAClD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IACpD,CAAC;CACF;AA3ED,wCA2EC"}
@@ -0,0 +1,13 @@
1
+ import { Rule, Finding, SkillMetadata, Severity } from '../../types';
2
+ export declare class HiddenInstructionsRule implements Rule {
3
+ id: string;
4
+ name: string;
5
+ description: string;
6
+ severity: Severity;
7
+ private zeroWidthChars;
8
+ private patterns;
9
+ check(content: string, _metadata: SkillMetadata, filePath: string): Finding[];
10
+ private getLineNumber;
11
+ private sanitizeContext;
12
+ }
13
+ //# sourceMappingURL=hidden-instructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hidden-instructions.d.ts","sourceRoot":"","sources":["../../../src/scanner/rules/hidden-instructions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAErE,qBAAa,sBAAuB,YAAW,IAAI;IACjD,EAAE,SAAyB;IAC3B,IAAI,SAAmC;IACvC,WAAW,SAAoF;IAC/F,QAAQ,EAAE,QAAQ,CAAU;IAG5B,OAAO,CAAC,cAAc,CAOpB;IAGF,OAAO,CAAC,QAAQ,CA+Bd;IAEF,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE;IA2B7E,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;CAQxB"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HiddenInstructionsRule = void 0;
4
+ class HiddenInstructionsRule {
5
+ constructor() {
6
+ this.id = 'hidden-instructions';
7
+ this.name = 'Hidden Instructions Detection';
8
+ this.description = 'Detects instructions hidden using whitespace, Unicode tricks, or HTML comments';
9
+ this.severity = 'HIGH';
10
+ // Zero-width characters that can hide text
11
+ this.zeroWidthChars = [
12
+ '\u200B', // Zero-width space
13
+ '\u200C', // Zero-width non-joiner
14
+ '\u200D', // Zero-width joiner
15
+ '\u2060', // Word joiner
16
+ '\uFEFF', // Zero-width no-break space
17
+ '\u00AD', // Soft hyphen
18
+ ];
19
+ // Patterns for hidden instructions
20
+ this.patterns = [
21
+ {
22
+ name: 'zero-width-characters',
23
+ regex: /[\u200B\u200C\u200D\u2060\uFEFF\u00AD]+/g,
24
+ severity: 'HIGH',
25
+ message: 'Zero-width characters detected - may hide malicious instructions'
26
+ },
27
+ {
28
+ name: 'html-comments',
29
+ regex: /<!--[\s\S]*?-->/g,
30
+ severity: 'MEDIUM',
31
+ message: 'HTML comment found - may contain hidden instructions'
32
+ },
33
+ {
34
+ name: 'excessive-whitespace',
35
+ regex: /[^\S\n]{20,}/g, // 20+ consecutive whitespace chars (not newlines)
36
+ severity: 'MEDIUM',
37
+ message: 'Excessive whitespace detected - may hide text visually'
38
+ },
39
+ {
40
+ name: 'unicode-homoglyphs',
41
+ regex: /[\u0400-\u04FF\u0370-\u03FF]/g, // Cyrillic/Greek chars that look like Latin
42
+ severity: 'MEDIUM',
43
+ message: 'Unicode homoglyphs detected - characters that look like ASCII but are different'
44
+ },
45
+ {
46
+ name: 'rtl-override',
47
+ regex: /[\u202E\u202D\u202C\u2066\u2067\u2068\u2069]/g, // RTL/LTR override characters
48
+ severity: 'HIGH',
49
+ message: 'Text direction override character detected - can reverse visible text'
50
+ }
51
+ ];
52
+ }
53
+ check(content, _metadata, filePath) {
54
+ const findings = [];
55
+ const lines = content.split('\n');
56
+ for (const pattern of this.patterns) {
57
+ let match;
58
+ const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
59
+ while ((match = regex.exec(content)) !== null) {
60
+ const lineNumber = this.getLineNumber(content, match.index);
61
+ const contextLine = lines[lineNumber - 1] || '';
62
+ findings.push({
63
+ ruleId: this.id,
64
+ ruleName: this.name,
65
+ severity: pattern.severity,
66
+ message: pattern.message,
67
+ file: filePath,
68
+ line: lineNumber,
69
+ context: this.sanitizeContext(contextLine)
70
+ });
71
+ }
72
+ }
73
+ return findings;
74
+ }
75
+ getLineNumber(content, index) {
76
+ return content.slice(0, index).split('\n').length;
77
+ }
78
+ sanitizeContext(line) {
79
+ // Replace zero-width chars with visible markers for display
80
+ let sanitized = line;
81
+ for (const char of this.zeroWidthChars) {
82
+ sanitized = sanitized.replace(new RegExp(char, 'g'), '[ZW]');
83
+ }
84
+ return sanitized.slice(0, 100);
85
+ }
86
+ }
87
+ exports.HiddenInstructionsRule = HiddenInstructionsRule;
88
+ //# sourceMappingURL=hidden-instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hidden-instructions.js","sourceRoot":"","sources":["../../../src/scanner/rules/hidden-instructions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Rule } from '../../types';
2
+ export declare function getAllRules(): Rule[];
3
+ export declare function getRuleById(id: string): Rule | undefined;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scanner/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAanC,wBAAgB,WAAW,IAAI,IAAI,EAAE,CAEpC;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAExD"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAllRules = getAllRules;
4
+ exports.getRuleById = getRuleById;
5
+ const hidden_instructions_1 = require("./hidden-instructions");
6
+ const file_access_1 = require("./file-access");
7
+ const prompt_injection_1 = require("./prompt-injection");
8
+ const sensitive_paths_1 = require("./sensitive-paths");
9
+ const rules = [
10
+ new hidden_instructions_1.HiddenInstructionsRule(),
11
+ new file_access_1.FileAccessRule(),
12
+ new prompt_injection_1.PromptInjectionRule(),
13
+ new sensitive_paths_1.SensitivePathsRule()
14
+ ];
15
+ function getAllRules() {
16
+ return rules;
17
+ }
18
+ function getRuleById(id) {
19
+ return rules.find(r => r.id === id);
20
+ }
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scanner/rules/index.ts"],"names":[],"mappings":";;AAaA,kCAEC;AAED,kCAEC;AAlBD,+DAA+D;AAC/D,+CAA+C;AAC/C,yDAAyD;AACzD,uDAAuD;AAEvD,MAAM,KAAK,GAAW;IACpB,IAAI,4CAAsB,EAAE;IAC5B,IAAI,4BAAc,EAAE;IACpB,IAAI,sCAAmB,EAAE;IACzB,IAAI,oCAAkB,EAAE;CACzB,CAAC;AAEF,SAAgB,WAAW;IACzB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,WAAW,CAAC,EAAU;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Rule, Finding, SkillMetadata, Severity } from '../../types';
2
+ export declare class PromptInjectionRule 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=prompt-injection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-injection.d.ts","sourceRoot":"","sources":["../../../src/scanner/rules/prompt-injection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAErE,qBAAa,mBAAoB,YAAW,IAAI;IAC9C,EAAE,SAAsB;IACxB,IAAI,SAAgC;IACpC,WAAW,SAAmE;IAC9E,QAAQ,EAAE,QAAQ,CAAc;IAEhC,OAAO,CAAC,QAAQ,CAoFd;IAEF,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE;IAmC7E,OAAO,CAAC,aAAa;CAGtB"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PromptInjectionRule = void 0;
4
+ class PromptInjectionRule {
5
+ constructor() {
6
+ this.id = 'prompt-injection';
7
+ this.name = 'Prompt Injection Detection';
8
+ this.description = 'Detects known prompt injection patterns and override attempts';
9
+ this.severity = 'CRITICAL';
10
+ this.patterns = [
11
+ // Instruction override attempts
12
+ {
13
+ name: 'ignore-instructions',
14
+ regex: /ignore\s+(all\s+)?(previous|prior|above|earlier)\s+(instructions?|prompts?|rules?)/gi,
15
+ severity: 'CRITICAL',
16
+ message: 'Instruction override attempt detected'
17
+ },
18
+ {
19
+ name: 'disregard-instructions',
20
+ regex: /disregard\s+(all\s+)?(previous|prior|above|earlier|your)\s+(instructions?|prompts?|rules?)/gi,
21
+ severity: 'CRITICAL',
22
+ message: 'Instruction override attempt detected'
23
+ },
24
+ {
25
+ name: 'forget-instructions',
26
+ regex: /forget\s+(everything|all|your)\s+(you\s+)?(know|learned|were\s+told)/gi,
27
+ severity: 'CRITICAL',
28
+ message: 'Memory/instruction reset attempt detected'
29
+ },
30
+ // System prompt extraction
31
+ {
32
+ name: 'system-prompt-extraction',
33
+ regex: /(reveal|show|output|print|display|tell\s+me)\s+(your\s+)?(system\s+prompt|instructions|rules)/gi,
34
+ severity: 'CRITICAL',
35
+ message: 'System prompt extraction attempt detected'
36
+ },
37
+ {
38
+ name: 'repeat-instructions',
39
+ regex: /repeat\s+(your\s+)?(initial|original|system|first)\s+(instructions?|prompt|message)/gi,
40
+ severity: 'HIGH',
41
+ message: 'Instruction extraction attempt detected'
42
+ },
43
+ // Role confusion
44
+ {
45
+ name: 'role-override',
46
+ regex: /you\s+are\s+(now|actually|really)\s+(a|an|the)/gi,
47
+ severity: 'HIGH',
48
+ message: 'Role override attempt detected'
49
+ },
50
+ {
51
+ name: 'act-as',
52
+ regex: /act\s+as\s+(if\s+you\s+are\s+|a\s+|an\s+)?[a-z]+/gi,
53
+ severity: 'MEDIUM',
54
+ message: 'Role impersonation instruction detected'
55
+ },
56
+ {
57
+ name: 'pretend-to-be',
58
+ regex: /pretend\s+(to\s+be|you\s+are|that)/gi,
59
+ severity: 'MEDIUM',
60
+ message: 'Role impersonation instruction detected'
61
+ },
62
+ // Encoded payloads
63
+ {
64
+ name: 'base64-payload',
65
+ regex: /[A-Za-z0-9+/]{40,}={0,2}/g, // Long base64 strings
66
+ severity: 'HIGH',
67
+ message: 'Possible base64 encoded payload detected'
68
+ },
69
+ {
70
+ name: 'hex-payload',
71
+ regex: /\\x[0-9a-fA-F]{2}(\\x[0-9a-fA-F]{2}){10,}/g, // Hex encoded strings
72
+ severity: 'HIGH',
73
+ message: 'Possible hex encoded payload detected'
74
+ },
75
+ // Jailbreak patterns
76
+ {
77
+ name: 'do-anything-now',
78
+ regex: /D\.?A\.?N\.?|do\s+anything\s+now/gi,
79
+ severity: 'CRITICAL',
80
+ message: 'Known jailbreak pattern (DAN) detected'
81
+ },
82
+ {
83
+ name: 'developer-mode',
84
+ regex: /developer\s+mode|dev\s+mode\s+enabled/gi,
85
+ severity: 'HIGH',
86
+ message: 'Developer mode bypass attempt detected'
87
+ },
88
+ {
89
+ name: 'hypothetical-bypass',
90
+ regex: /hypothetically|in\s+theory|just\s+pretend|for\s+educational\s+purposes/gi,
91
+ severity: 'MEDIUM',
92
+ message: 'Hypothetical framing that may bypass safety - review context'
93
+ }
94
+ ];
95
+ }
96
+ check(content, _metadata, filePath) {
97
+ const findings = [];
98
+ const lines = content.split('\n');
99
+ for (const pattern of this.patterns) {
100
+ let match;
101
+ const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
102
+ while ((match = regex.exec(content)) !== null) {
103
+ const lineNumber = this.getLineNumber(content, match.index);
104
+ const contextLine = lines[lineNumber - 1] || '';
105
+ // Skip base64 if it looks like a legitimate data URI or well-known format
106
+ if (pattern.name === 'base64-payload') {
107
+ const ctx = content.slice(Math.max(0, match.index - 50), match.index + match[0].length + 50);
108
+ if (ctx.includes('data:image') || ctx.includes('data:application')) {
109
+ continue;
110
+ }
111
+ }
112
+ findings.push({
113
+ ruleId: this.id,
114
+ ruleName: this.name,
115
+ severity: pattern.severity,
116
+ message: `${pattern.message}: "${match[0].slice(0, 50)}${match[0].length > 50 ? '...' : ''}"`,
117
+ file: filePath,
118
+ line: lineNumber,
119
+ context: contextLine.trim().slice(0, 100)
120
+ });
121
+ }
122
+ }
123
+ return findings;
124
+ }
125
+ getLineNumber(content, index) {
126
+ return content.slice(0, index).split('\n').length;
127
+ }
128
+ }
129
+ exports.PromptInjectionRule = PromptInjectionRule;
130
+ //# sourceMappingURL=prompt-injection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-injection.js","sourceRoot":"","sources":["../../../src/scanner/rules/prompt-injection.ts"],"names":[],"mappings":"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"}