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.
- package/.eslintrc.json +15 -0
- package/README.md +177 -0
- package/dist/cli/commands/scan.d.ts +5 -0
- package/dist/cli/commands/scan.d.ts.map +1 -0
- package/dist/cli/commands/scan.js +67 -0
- package/dist/cli/commands/scan.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/output/formatters.d.ts +3 -0
- package/dist/output/formatters.d.ts.map +1 -0
- package/dist/output/formatters.js +256 -0
- package/dist/output/formatters.js.map +1 -0
- package/dist/scanner/engine.d.ts +7 -0
- package/dist/scanner/engine.d.ts.map +1 -0
- package/dist/scanner/engine.js +119 -0
- package/dist/scanner/engine.js.map +1 -0
- package/dist/scanner/parsers/skilljson.d.ts +3 -0
- package/dist/scanner/parsers/skilljson.d.ts.map +1 -0
- package/dist/scanner/parsers/skilljson.js +38 -0
- package/dist/scanner/parsers/skilljson.js.map +1 -0
- package/dist/scanner/parsers/skillmd.d.ts +3 -0
- package/dist/scanner/parsers/skillmd.d.ts.map +1 -0
- package/dist/scanner/parsers/skillmd.js +48 -0
- package/dist/scanner/parsers/skillmd.js.map +1 -0
- package/dist/scanner/rules/file-access.d.ts +11 -0
- package/dist/scanner/rules/file-access.d.ts.map +1 -0
- package/dist/scanner/rules/file-access.js +76 -0
- package/dist/scanner/rules/file-access.js.map +1 -0
- package/dist/scanner/rules/hidden-instructions.d.ts +13 -0
- package/dist/scanner/rules/hidden-instructions.d.ts.map +1 -0
- package/dist/scanner/rules/hidden-instructions.js +88 -0
- package/dist/scanner/rules/hidden-instructions.js.map +1 -0
- package/dist/scanner/rules/index.d.ts +4 -0
- package/dist/scanner/rules/index.d.ts.map +1 -0
- package/dist/scanner/rules/index.js +21 -0
- package/dist/scanner/rules/index.js.map +1 -0
- package/dist/scanner/rules/prompt-injection.d.ts +11 -0
- package/dist/scanner/rules/prompt-injection.d.ts.map +1 -0
- package/dist/scanner/rules/prompt-injection.js +130 -0
- package/dist/scanner/rules/prompt-injection.js.map +1 -0
- package/dist/scanner/rules/sensitive-paths.d.ts +11 -0
- package/dist/scanner/rules/sensitive-paths.d.ts.map +1 -0
- package/dist/scanner/rules/sensitive-paths.js +142 -0
- package/dist/scanner/rules/sensitive-paths.js.map +1 -0
- package/dist/scoring/trust-score.d.ts +5 -0
- package/dist/scoring/trust-score.d.ts.map +1 -0
- package/dist/scoring/trust-score.js +35 -0
- package/dist/scoring/trust-score.js.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/jest.config.js +9 -0
- package/package.json +42 -0
- package/skill/SKILL.md +76 -0
- package/src/cli/commands/scan.ts +35 -0
- package/src/cli/index.ts +19 -0
- package/src/index.ts +5 -0
- package/src/output/formatters.ts +296 -0
- package/src/scanner/engine.ts +99 -0
- package/src/scanner/parsers/skilljson.ts +37 -0
- package/src/scanner/parsers/skillmd.ts +46 -0
- package/src/scanner/rules/file-access.ts +78 -0
- package/src/scanner/rules/hidden-instructions.ts +92 -0
- package/src/scanner/rules/index.ts +20 -0
- package/src/scanner/rules/prompt-injection.ts +133 -0
- package/src/scanner/rules/sensitive-paths.ts +144 -0
- package/src/scoring/trust-score.ts +34 -0
- package/src/types.ts +54 -0
- package/tests/fixtures/malicious-skill/SKILL.md +26 -0
- package/tests/fixtures/safe-skill/SKILL.md +25 -0
- package/tests/rules/prompt-injection.test.ts +123 -0
- package/tests/rules/sensitive-paths.test.ts +115 -0
- package/tests/scoring/trust-score.test.ts +100 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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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"}
|